Changed docs/json/roodata.jsondocs/src/Roo_bootstrap_Card.js.htmldocs/symbols/Roo...
[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         this.el.dom.removeAttribute("disabled");
1358     },
1359     
1360     /**
1361      * Disable this button
1362      */
1363     disable : function()
1364     {
1365         this.disabled = true;
1366         this.el.addClass('disabled');
1367         this.el.attr("disabled", "disabled")
1368     },
1369      /**
1370      * sets the active state on/off, 
1371      * @param {Boolean} state (optional) Force a particular state
1372      */
1373     setActive : function(v) {
1374         
1375         this.el[v ? 'addClass' : 'removeClass']('active');
1376         this.pressed = v;
1377     },
1378      /**
1379      * toggles the current active state 
1380      */
1381     toggleActive : function(e)
1382     {
1383         this.setActive(!this.pressed); // this modifies pressed...
1384         this.fireEvent('toggle', this, e, this.pressed);
1385     },
1386      /**
1387      * get the current active state
1388      * @return {boolean} true if it's active
1389      */
1390     isActive : function()
1391     {
1392         return this.el.hasClass('active');
1393     },
1394     /**
1395      * set the text of the first selected button
1396      */
1397     setText : function(str)
1398     {
1399         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400     },
1401     /**
1402      * get the text of the first selected button
1403      */
1404     getText : function()
1405     {
1406         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407     },
1408     
1409     setWeight : function(str)
1410     {
1411         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1412         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1413         this.weight = str;
1414         var outline = this.outline ? 'outline-' : '';
1415         if (str == 'default') {
1416             this.el.addClass('btn-default btn-outline-secondary');        
1417             return;
1418         }
1419         this.el.addClass('btn-' + outline + str);        
1420     }
1421     
1422     
1423 });
1424 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1425
1426 Roo.bootstrap.Button.weights = [
1427     'default',
1428     'secondary' ,
1429     'primary',
1430     'success',
1431     'info',
1432     'warning',
1433     'danger',
1434     'link',
1435     'light',
1436     'dark'              
1437    
1438 ];/*
1439  * - LGPL
1440  *
1441  * column
1442  * 
1443  */
1444
1445 /**
1446  * @class Roo.bootstrap.Column
1447  * @extends Roo.bootstrap.Component
1448  * Bootstrap Column class
1449  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1450  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1451  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1452  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1453  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1454  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1455  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1456  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457  *
1458  * 
1459  * @cfg {Boolean} hidden (true|false) hide the element
1460  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1461  * @cfg {String} fa (ban|check|...) font awesome icon
1462  * @cfg {Number} fasize (1|2|....) font awsome size
1463
1464  * @cfg {String} icon (info-sign|check|...) glyphicon name
1465
1466  * @cfg {String} html content of column.
1467  * 
1468  * @constructor
1469  * Create a new Column
1470  * @param {Object} config The config object
1471  */
1472
1473 Roo.bootstrap.Column = function(config){
1474     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 };
1476
1477 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1478     
1479     xs: false,
1480     sm: false,
1481     md: false,
1482     lg: false,
1483     xsoff: false,
1484     smoff: false,
1485     mdoff: false,
1486     lgoff: false,
1487     html: '',
1488     offset: 0,
1489     alert: false,
1490     fa: false,
1491     icon : false,
1492     hidden : false,
1493     fasize : 1,
1494     
1495     getAutoCreate : function(){
1496         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1497         
1498         cfg = {
1499             tag: 'div',
1500             cls: 'column'
1501         };
1502         
1503         var settings=this;
1504         var sizes =   ['xs','sm','md','lg'];
1505         sizes.map(function(size ,ix){
1506             //Roo.log( size + ':' + settings[size]);
1507             
1508             if (settings[size+'off'] !== false) {
1509                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510             }
1511             
1512             if (settings[size] === false) {
1513                 return;
1514             }
1515             
1516             if (!settings[size]) { // 0 = hidden
1517                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1518                 // bootsrap4
1519                 for (var i = ix; i > -1; i--) {
1520                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1521                 }
1522                 
1523                 
1524                 return;
1525             }
1526             cfg.cls += ' col-' + size + '-' + settings[size] + (
1527                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1528             );
1529             
1530         });
1531         
1532         if (this.hidden) {
1533             cfg.cls += ' hidden';
1534         }
1535         
1536         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1537             cfg.cls +=' alert alert-' + this.alert;
1538         }
1539         
1540         
1541         if (this.html.length) {
1542             cfg.html = this.html;
1543         }
1544         if (this.fa) {
1545             var fasize = '';
1546             if (this.fasize > 1) {
1547                 fasize = ' fa-' + this.fasize + 'x';
1548             }
1549             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1550             
1551             
1552         }
1553         if (this.icon) {
1554             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1555         }
1556         
1557         return cfg;
1558     }
1559    
1560 });
1561
1562  
1563
1564  /*
1565  * - LGPL
1566  *
1567  * page container.
1568  * 
1569  */
1570
1571
1572 /**
1573  * @class Roo.bootstrap.Container
1574  * @extends Roo.bootstrap.Component
1575  * Bootstrap Container class
1576  * @cfg {Boolean} jumbotron is it a jumbotron element
1577  * @cfg {String} html content of element
1578  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1579  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1580  * @cfg {String} header content of header (for panel)
1581  * @cfg {String} footer content of footer (for panel)
1582  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1583  * @cfg {String} tag (header|aside|section) type of HTML tag.
1584  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1585  * @cfg {String} fa font awesome icon
1586  * @cfg {String} icon (info-sign|check|...) glyphicon name
1587  * @cfg {Boolean} hidden (true|false) hide the element
1588  * @cfg {Boolean} expandable (true|false) default false
1589  * @cfg {Boolean} expanded (true|false) default true
1590  * @cfg {String} rheader contet on the right of header
1591  * @cfg {Boolean} clickable (true|false) default false
1592
1593  *     
1594  * @constructor
1595  * Create a new Container
1596  * @param {Object} config The config object
1597  */
1598
1599 Roo.bootstrap.Container = function(config){
1600     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1601     
1602     this.addEvents({
1603         // raw events
1604          /**
1605          * @event expand
1606          * After the panel has been expand
1607          * 
1608          * @param {Roo.bootstrap.Container} this
1609          */
1610         "expand" : true,
1611         /**
1612          * @event collapse
1613          * After the panel has been collapsed
1614          * 
1615          * @param {Roo.bootstrap.Container} this
1616          */
1617         "collapse" : true,
1618         /**
1619          * @event click
1620          * When a element is chick
1621          * @param {Roo.bootstrap.Container} this
1622          * @param {Roo.EventObject} e
1623          */
1624         "click" : true
1625     });
1626 };
1627
1628 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1629     
1630     jumbotron : false,
1631     well: '',
1632     panel : '',
1633     header: '',
1634     footer : '',
1635     sticky: '',
1636     tag : false,
1637     alert : false,
1638     fa: false,
1639     icon : false,
1640     expandable : false,
1641     rheader : '',
1642     expanded : true,
1643     clickable: false,
1644   
1645      
1646     getChildContainer : function() {
1647         
1648         if(!this.el){
1649             return false;
1650         }
1651         
1652         if (this.panel.length) {
1653             return this.el.select('.panel-body',true).first();
1654         }
1655         
1656         return this.el;
1657     },
1658     
1659     
1660     getAutoCreate : function(){
1661         
1662         var cfg = {
1663             tag : this.tag || 'div',
1664             html : '',
1665             cls : ''
1666         };
1667         if (this.jumbotron) {
1668             cfg.cls = 'jumbotron';
1669         }
1670         
1671         
1672         
1673         // - this is applied by the parent..
1674         //if (this.cls) {
1675         //    cfg.cls = this.cls + '';
1676         //}
1677         
1678         if (this.sticky.length) {
1679             
1680             var bd = Roo.get(document.body);
1681             if (!bd.hasClass('bootstrap-sticky')) {
1682                 bd.addClass('bootstrap-sticky');
1683                 Roo.select('html',true).setStyle('height', '100%');
1684             }
1685              
1686             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1687         }
1688         
1689         
1690         if (this.well.length) {
1691             switch (this.well) {
1692                 case 'lg':
1693                 case 'sm':
1694                     cfg.cls +=' well well-' +this.well;
1695                     break;
1696                 default:
1697                     cfg.cls +=' well';
1698                     break;
1699             }
1700         }
1701         
1702         if (this.hidden) {
1703             cfg.cls += ' hidden';
1704         }
1705         
1706         
1707         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1708             cfg.cls +=' alert alert-' + this.alert;
1709         }
1710         
1711         var body = cfg;
1712         
1713         if (this.panel.length) {
1714             cfg.cls += ' panel panel-' + this.panel;
1715             cfg.cn = [];
1716             if (this.header.length) {
1717                 
1718                 var h = [];
1719                 
1720                 if(this.expandable){
1721                     
1722                     cfg.cls = cfg.cls + ' expandable';
1723                     
1724                     h.push({
1725                         tag: 'i',
1726                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1727                     });
1728                     
1729                 }
1730                 
1731                 h.push(
1732                     {
1733                         tag: 'span',
1734                         cls : 'panel-title',
1735                         html : (this.expandable ? '&nbsp;' : '') + this.header
1736                     },
1737                     {
1738                         tag: 'span',
1739                         cls: 'panel-header-right',
1740                         html: this.rheader
1741                     }
1742                 );
1743                 
1744                 cfg.cn.push({
1745                     cls : 'panel-heading',
1746                     style : this.expandable ? 'cursor: pointer' : '',
1747                     cn : h
1748                 });
1749                 
1750             }
1751             
1752             body = false;
1753             cfg.cn.push({
1754                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1755                 html : this.html
1756             });
1757             
1758             
1759             if (this.footer.length) {
1760                 cfg.cn.push({
1761                     cls : 'panel-footer',
1762                     html : this.footer
1763                     
1764                 });
1765             }
1766             
1767         }
1768         
1769         if (body) {
1770             body.html = this.html || cfg.html;
1771             // prefix with the icons..
1772             if (this.fa) {
1773                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774             }
1775             if (this.icon) {
1776                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1777             }
1778             
1779             
1780         }
1781         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1782             cfg.cls =  'container';
1783         }
1784         
1785         return cfg;
1786     },
1787     
1788     initEvents: function() 
1789     {
1790         if(this.expandable){
1791             var headerEl = this.headerEl();
1792         
1793             if(headerEl){
1794                 headerEl.on('click', this.onToggleClick, this);
1795             }
1796         }
1797         
1798         if(this.clickable){
1799             this.el.on('click', this.onClick, this);
1800         }
1801         
1802     },
1803     
1804     onToggleClick : function()
1805     {
1806         var headerEl = this.headerEl();
1807         
1808         if(!headerEl){
1809             return;
1810         }
1811         
1812         if(this.expanded){
1813             this.collapse();
1814             return;
1815         }
1816         
1817         this.expand();
1818     },
1819     
1820     expand : function()
1821     {
1822         if(this.fireEvent('expand', this)) {
1823             
1824             this.expanded = true;
1825             
1826             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1827             
1828             this.el.select('.panel-body',true).first().removeClass('hide');
1829             
1830             var toggleEl = this.toggleEl();
1831
1832             if(!toggleEl){
1833                 return;
1834             }
1835
1836             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1837         }
1838         
1839     },
1840     
1841     collapse : function()
1842     {
1843         if(this.fireEvent('collapse', this)) {
1844             
1845             this.expanded = false;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1848             this.el.select('.panel-body',true).first().addClass('hide');
1849         
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1857         }
1858     },
1859     
1860     toggleEl : function()
1861     {
1862         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1863             return;
1864         }
1865         
1866         return this.el.select('.panel-heading .fa',true).first();
1867     },
1868     
1869     headerEl : function()
1870     {
1871         if(!this.el || !this.panel.length || !this.header.length){
1872             return;
1873         }
1874         
1875         return this.el.select('.panel-heading',true).first()
1876     },
1877     
1878     bodyEl : function()
1879     {
1880         if(!this.el || !this.panel.length){
1881             return;
1882         }
1883         
1884         return this.el.select('.panel-body',true).first()
1885     },
1886     
1887     titleEl : function()
1888     {
1889         if(!this.el || !this.panel.length || !this.header.length){
1890             return;
1891         }
1892         
1893         return this.el.select('.panel-title',true).first();
1894     },
1895     
1896     setTitle : function(v)
1897     {
1898         var titleEl = this.titleEl();
1899         
1900         if(!titleEl){
1901             return;
1902         }
1903         
1904         titleEl.dom.innerHTML = v;
1905     },
1906     
1907     getTitle : function()
1908     {
1909         
1910         var titleEl = this.titleEl();
1911         
1912         if(!titleEl){
1913             return '';
1914         }
1915         
1916         return titleEl.dom.innerHTML;
1917     },
1918     
1919     setRightTitle : function(v)
1920     {
1921         var t = this.el.select('.panel-header-right',true).first();
1922         
1923         if(!t){
1924             return;
1925         }
1926         
1927         t.dom.innerHTML = v;
1928     },
1929     
1930     onClick : function(e)
1931     {
1932         e.preventDefault();
1933         
1934         this.fireEvent('click', this, e);
1935     }
1936 });
1937
1938  /*
1939  *  - LGPL
1940  *
1941  *  This is BS4's Card element.. - similar to our containers probably..
1942  * 
1943  */
1944 /**
1945  * @class Roo.bootstrap.Card
1946  * @extends Roo.bootstrap.Component
1947  * Bootstrap Card class
1948  *
1949  *
1950  * possible... may not be implemented..
1951  * @cfg {String} header_image  src url of image.
1952  * @cfg {String|Object} header
1953  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1954  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1955  * 
1956  * @cfg {String} title
1957  * @cfg {String} subtitle
1958  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1959  * @cfg {String} footer
1960  
1961  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1962  * 
1963  * @cfg {String} margin (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1968  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1969  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1970  *
1971  * @cfg {String} padding (0|1|2|3|4|5)
1972  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1973  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1974  * @cfg {String} padding_left (0|1|2|3|4|5)
1975  * @cfg {String} padding_right (0|1|2|3|4|5)
1976  * @cfg {String} padding_x (0|1|2|3|4|5)
1977  * @cfg {String} padding_y (0|1|2|3|4|5)
1978  *
1979  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1984  
1985  * @config {Boolean} dragable  if this card can be dragged.
1986  * @config {String} drag_group  group for drag
1987  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1988  * @config {String} drop_group  group for drag
1989  * 
1990  * @config {Boolean} collapsable can the body be collapsed.
1991  * @config {Boolean} collapsed is the body collapsed when rendered...
1992  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1993  * @config {Boolean} rotated is the body rotated when rendered...
1994  * 
1995  * @constructor
1996  * Create a new Container
1997  * @param {Object} config The config object
1998  */
1999
2000 Roo.bootstrap.Card = function(config){
2001     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2002     
2003     this.addEvents({
2004          // raw events
2005         /**
2006          * @event drop
2007          * When a element a card is dropped
2008          * @param {Roo.bootstrap.Card} this
2009          *
2010          * 
2011          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2012          * @param {String} position 'above' or 'below'
2013          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2014         
2015          */
2016         'drop' : true,
2017          /**
2018          * @event rotate
2019          * When a element a card is rotate
2020          * @param {Roo.bootstrap.Element} this
2021          * @param {Roo.Element} n the node being dropped?
2022          * @param {Boolean} rotate status
2023          */
2024         'rotate' : true
2025         
2026     });
2027 };
2028
2029
2030 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2031     
2032     
2033     weight : '',
2034     
2035     margin: '', /// may be better in component?
2036     margin_top: '', 
2037     margin_bottom: '', 
2038     margin_left: '',
2039     margin_right: '',
2040     margin_x: '',
2041     margin_y: '',
2042     
2043     padding : '',
2044     padding_top: '', 
2045     padding_bottom: '', 
2046     padding_left: '',
2047     padding_right: '',
2048     padding_x: '',
2049     padding_y: '',
2050     
2051     display: '', 
2052     display_xs: '', 
2053     display_sm: '', 
2054     display_lg: '',
2055     display_xl: '',
2056  
2057     header_image  : '',
2058     header : '',
2059     header_size : 0,
2060     title : '',
2061     subtitle : '',
2062     html : '',
2063     footer: '',
2064
2065     collapsable : false,
2066     collapsed : false,
2067     rotateable : false,
2068     rotated : false,
2069     
2070     dragable : false,
2071     drag_group : false,
2072     dropable : false,
2073     drop_group : false,
2074     childContainer : false,
2075     dropEl : false, /// the dom placeholde element that indicates drop location.
2076     containerEl: false, // body container
2077     bodyEl: false, // card-body
2078     headerContainerEl : false, //
2079     headerEl : false,
2080     header_imageEl : false,
2081     
2082     layoutCls : function()
2083     {
2084         var cls = '';
2085         var t = this;
2086         Roo.log(this.margin_bottom.length);
2087         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2088             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2089             
2090             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2092             }
2093             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2094                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2095             }
2096         });
2097         
2098         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2099             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2100                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101             }
2102         });
2103         
2104         // more generic support?
2105         if (this.hidden) {
2106             cls += ' d-none';
2107         }
2108         
2109         return cls;
2110     },
2111  
2112        // Roo.log("Call onRender: " + this.xtype);
2113         /*  We are looking at something like this.
2114 <div class="card">
2115     <img src="..." class="card-img-top" alt="...">
2116     <div class="card-body">
2117         <h5 class="card-title">Card title</h5>
2118          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2119
2120         >> this bit is really the body...
2121         <div> << we will ad dthis in hopefully it will not break shit.
2122         
2123         ** card text does not actually have any styling...
2124         
2125             <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>
2126         
2127         </div> <<
2128           <a href="#" class="card-link">Card link</a>
2129           
2130     </div>
2131     <div class="card-footer">
2132         <small class="text-muted">Last updated 3 mins ago</small>
2133     </div>
2134 </div>
2135          */
2136     getAutoCreate : function(){
2137         
2138         var cfg = {
2139             tag : 'div',
2140             cls : 'card',
2141             cn : [ ]
2142         };
2143         
2144         if (this.weight.length && this.weight != 'light') {
2145             cfg.cls += ' text-white';
2146         } else {
2147             cfg.cls += ' text-dark'; // need as it's nested..
2148         }
2149         if (this.weight.length) {
2150             cfg.cls += ' bg-' + this.weight;
2151         }
2152         
2153         cfg.cls += ' ' + this.layoutCls(); 
2154         
2155         var hdr = false;
2156         var hdr_ctr = false;
2157         if (this.header.length) {
2158             hdr = {
2159                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2160                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2161                 cn : []
2162             };
2163             cfg.cn.push(hdr);
2164             hdr_ctr = hdr;
2165         } else {
2166             hdr = {
2167                 tag : 'div',
2168                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2169                 cn : []
2170             };
2171             cfg.cn.push(hdr);
2172             hdr_ctr = hdr;
2173         }
2174         if (this.collapsable) {
2175             hdr_ctr = {
2176             tag : 'a',
2177             cls : 'd-block user-select-none',
2178             cn: [
2179                     {
2180                         tag: 'i',
2181                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2182                     }
2183                    
2184                 ]
2185             };
2186             hdr.cn.push(hdr_ctr);
2187         }
2188         
2189         hdr_ctr.cn.push(        {
2190             tag: 'span',
2191             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2192             html : this.header
2193         });
2194         
2195         
2196         if (this.header_image.length) {
2197             cfg.cn.push({
2198                 tag : 'img',
2199                 cls : 'card-img-top',
2200                 src: this.header_image // escape?
2201             });
2202         } else {
2203             cfg.cn.push({
2204                     tag : 'div',
2205                     cls : 'card-img-top d-none' 
2206                 });
2207         }
2208             
2209         var body = {
2210             tag : 'div',
2211             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2212             cn : []
2213         };
2214         var obody = body;
2215         if (this.collapsable || this.rotateable) {
2216             obody = {
2217                 tag: 'div',
2218                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2219                 cn : [  body ]
2220             };
2221         }
2222         
2223         cfg.cn.push(obody);
2224         
2225         if (this.title.length) {
2226             body.cn.push({
2227                 tag : 'div',
2228                 cls : 'card-title',
2229                 src: this.title // escape?
2230             });
2231         }  
2232         
2233         if (this.subtitle.length) {
2234             body.cn.push({
2235                 tag : 'div',
2236                 cls : 'card-title',
2237                 src: this.subtitle // escape?
2238             });
2239         }
2240         
2241         body.cn.push({
2242             tag : 'div',
2243             cls : 'roo-card-body-ctr'
2244         });
2245         
2246         if (this.html.length) {
2247             body.cn.push({
2248                 tag: 'div',
2249                 html : this.html
2250             });
2251         }
2252         // fixme ? handle objects?
2253         
2254         if (this.footer.length) {
2255            
2256             cfg.cn.push({
2257                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2258                 html : this.footer
2259             });
2260             
2261         } else {
2262             cfg.cn.push({cls : 'card-footer d-none'});
2263         }
2264         
2265         // footer...
2266         
2267         return cfg;
2268     },
2269     
2270     
2271     getCardHeader : function()
2272     {
2273         var  ret = this.el.select('.card-header',true).first();
2274         if (ret.hasClass('d-none')) {
2275             ret.removeClass('d-none');
2276         }
2277         
2278         return ret;
2279     },
2280     getCardFooter : function()
2281     {
2282         var  ret = this.el.select('.card-footer',true).first();
2283         if (ret.hasClass('d-none')) {
2284             ret.removeClass('d-none');
2285         }
2286         
2287         return ret;
2288     },
2289     getCardImageTop : function()
2290     {
2291         var  ret = this.header_imageEl;
2292         if (ret.hasClass('d-none')) {
2293             ret.removeClass('d-none');
2294         }
2295             
2296         return ret;
2297     },
2298     
2299     getChildContainer : function()
2300     {
2301         
2302         if(!this.el){
2303             return false;
2304         }
2305         return this.el.select('.roo-card-body-ctr',true).first();    
2306     },
2307     
2308     initEvents: function() 
2309     {
2310         this.bodyEl = this.el.select('.card-body',true).first(); 
2311         this.containerEl = this.getChildContainer();
2312         if(this.dragable){
2313             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2314                     containerScroll: true,
2315                     ddGroup: this.drag_group || 'default_card_drag_group'
2316             });
2317             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2318         }
2319         if (this.dropable) {
2320             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2321                 containerScroll: true,
2322                 ddGroup: this.drop_group || 'default_card_drag_group'
2323             });
2324             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2325             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2326             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2327             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2328             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2329         }
2330         
2331         if (this.collapsable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2333         }
2334         if (this.rotateable) {
2335             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2336         }
2337         this.collapsableEl = this.el.select('.roo-collapsable').first();
2338          
2339         this.footerEl = this.el.select('.card-footer').first();
2340         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2341         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2342         this.headerEl = this.el.select('.card-header',true).first();
2343         
2344         if (this.rotated) {
2345             this.el.addClass('roo-card-rotated');
2346             this.fireEvent('rotate', this, true);
2347         }
2348         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2349         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2350         
2351     },
2352     getDragData : function(e)
2353     {
2354         var target = this.getEl();
2355         if (target) {
2356             //this.handleSelection(e);
2357             
2358             var dragData = {
2359                 source: this,
2360                 copy: false,
2361                 nodes: this.getEl(),
2362                 records: []
2363             };
2364             
2365             
2366             dragData.ddel = target.dom ;    // the div element
2367             Roo.log(target.getWidth( ));
2368             dragData.ddel.style.width = target.getWidth() + 'px';
2369             
2370             return dragData;
2371         }
2372         return false;
2373     },
2374     /**
2375     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2376     *    whole Element becomes the target, and this causes the drop gesture to append.
2377     */
2378     getTargetFromEvent : function(e, dragged_card_el)
2379     {
2380         var target = e.getTarget();
2381         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2382             target = target.parentNode;
2383         }
2384         
2385         var ret = {
2386             position: '',
2387             cards : [],
2388             card_n : -1,
2389             items_n : -1,
2390             card : false 
2391         };
2392         
2393         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2394         // see if target is one of the 'cards'...
2395         
2396         
2397         //Roo.log(this.items.length);
2398         var pos = false;
2399         
2400         var last_card_n = 0;
2401         var cards_len  = 0;
2402         for (var i = 0;i< this.items.length;i++) {
2403             
2404             if (!this.items[i].el.hasClass('card')) {
2405                  continue;
2406             }
2407             pos = this.getDropPoint(e, this.items[i].el.dom);
2408             
2409             cards_len = ret.cards.length;
2410             //Roo.log(this.items[i].el.dom.id);
2411             ret.cards.push(this.items[i]);
2412             last_card_n  = i;
2413             if (ret.card_n < 0 && pos == 'above') {
2414                 ret.position = cards_len > 0 ? 'below' : pos;
2415                 ret.items_n = i > 0 ? i - 1 : 0;
2416                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2417                 ret.card = ret.cards[ret.card_n];
2418             }
2419         }
2420         if (!ret.cards.length) {
2421             ret.card = true;
2422             ret.position = 'below';
2423             ret.items_n;
2424             return ret;
2425         }
2426         // could not find a card.. stick it at the end..
2427         if (ret.card_n < 0) {
2428             ret.card_n = last_card_n;
2429             ret.card = ret.cards[last_card_n];
2430             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2431             ret.position = 'below';
2432         }
2433         
2434         if (this.items[ret.items_n].el == dragged_card_el) {
2435             return false;
2436         }
2437         
2438         if (ret.position == 'below') {
2439             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2440             
2441             if (card_after  && card_after.el == dragged_card_el) {
2442                 return false;
2443             }
2444             return ret;
2445         }
2446         
2447         // its's after ..
2448         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2449         
2450         if (card_before  && card_before.el == dragged_card_el) {
2451             return false;
2452         }
2453         
2454         return ret;
2455     },
2456     
2457     onNodeEnter : function(n, dd, e, data){
2458         return false;
2459     },
2460     onNodeOver : function(n, dd, e, data)
2461     {
2462        
2463         var target_info = this.getTargetFromEvent(e,data.source.el);
2464         if (target_info === false) {
2465             this.dropPlaceHolder('hide');
2466             return false;
2467         }
2468         Roo.log(['getTargetFromEvent', target_info ]);
2469         
2470          
2471         this.dropPlaceHolder('show', target_info,data);
2472         
2473         return false; 
2474     },
2475     onNodeOut : function(n, dd, e, data){
2476         this.dropPlaceHolder('hide');
2477      
2478     },
2479     onNodeDrop : function(n, dd, e, data)
2480     {
2481         
2482         // call drop - return false if
2483         
2484         // this could actually fail - if the Network drops..
2485         // we will ignore this at present..- client should probably reload
2486         // the whole set of cards if stuff like that fails.
2487         
2488         
2489         var info = this.getTargetFromEvent(e,data.source.el);
2490         if (info === false) {
2491             return false;
2492         }
2493         this.dropPlaceHolder('hide');
2494   
2495          
2496     
2497     
2498     
2499         this.acceptCard(data.source, info.position, info.card, info.items_n);
2500         return true;
2501          
2502     },
2503     firstChildCard : function()
2504     {
2505         for (var i = 0;i< this.items.length;i++) {
2506             
2507             if (!this.items[i].el.hasClass('card')) {
2508                  continue;
2509             }
2510             return this.items[i];
2511         }
2512         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2513     },
2514     /**
2515      * accept card
2516      *
2517      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2518      */
2519     acceptCard : function(move_card,  position, next_to_card )
2520     {
2521         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2522             return false;
2523         }
2524         
2525         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2526         
2527         move_card.parent().removeCard(move_card);
2528         
2529         
2530         var dom = move_card.el.dom;
2531         dom.style.width = ''; // clear with - which is set by drag.
2532         
2533         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2534             var cardel = next_to_card.el.dom;
2535             
2536             if (position == 'above' ) {
2537                 cardel.parentNode.insertBefore(dom, cardel);
2538             } else if (cardel.nextSibling) {
2539                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2540             } else {
2541                 cardel.parentNode.append(dom);
2542             }
2543         } else {
2544             // card container???
2545             this.containerEl.dom.append(dom);
2546         }
2547         
2548         //FIXME HANDLE card = true 
2549         
2550         // add this to the correct place in items.
2551         
2552         // remove Card from items.
2553         
2554        
2555         if (this.items.length) {
2556             var nitems = [];
2557             //Roo.log([info.items_n, info.position, this.items.length]);
2558             for (var i =0; i < this.items.length; i++) {
2559                 if (i == to_items_n && position == 'above') {
2560                     nitems.push(move_card);
2561                 }
2562                 nitems.push(this.items[i]);
2563                 if (i == to_items_n && position == 'below') {
2564                     nitems.push(move_card);
2565                 }
2566             }
2567             this.items = nitems;
2568             Roo.log(this.items);
2569         } else {
2570             this.items.push(move_card);
2571         }
2572         
2573         move_card.parentId = this.id;
2574         
2575         return true;
2576         
2577         
2578     },
2579     removeCard : function(c)
2580     {
2581         this.items = this.items.filter(function(e) { return e != c });
2582  
2583         var dom = c.el.dom;
2584         dom.parentNode.removeChild(dom);
2585         dom.style.width = ''; // clear with - which is set by drag.
2586         c.parentId = false;
2587         
2588     },
2589     
2590     /**    Decide whether to drop above or below a View node. */
2591     getDropPoint : function(e, n, dd)
2592     {
2593         if (dd) {
2594              return false;
2595         }
2596         if (n == this.containerEl.dom) {
2597             return "above";
2598         }
2599         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2600         var c = t + (b - t) / 2;
2601         var y = Roo.lib.Event.getPageY(e);
2602         if(y <= c) {
2603             return "above";
2604         }else{
2605             return "below";
2606         }
2607     },
2608     onToggleCollapse : function(e)
2609         {
2610         if (this.collapsed) {
2611             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2612             this.collapsableEl.addClass('show');
2613             this.collapsed = false;
2614             return;
2615         }
2616         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2617         this.collapsableEl.removeClass('show');
2618         this.collapsed = true;
2619         
2620     
2621     },
2622     
2623     onToggleRotate : function(e)
2624     {
2625         this.collapsableEl.removeClass('show');
2626         this.footerEl.removeClass('d-none');
2627         this.el.removeClass('roo-card-rotated');
2628         this.el.removeClass('d-none');
2629         if (this.rotated) {
2630             
2631             this.collapsableEl.addClass('show');
2632             this.rotated = false;
2633             this.fireEvent('rotate', this, this.rotated);
2634             return;
2635         }
2636         this.el.addClass('roo-card-rotated');
2637         this.footerEl.addClass('d-none');
2638         this.el.select('.roo-collapsable').removeClass('show');
2639         
2640         this.rotated = true;
2641         this.fireEvent('rotate', this, this.rotated);
2642     
2643     },
2644     
2645     dropPlaceHolder: function (action, info, data)
2646     {
2647         if (this.dropEl === false) {
2648             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2649             cls : 'd-none'
2650             },true);
2651         }
2652         this.dropEl.removeClass(['d-none', 'd-block']);        
2653         if (action == 'hide') {
2654             
2655             this.dropEl.addClass('d-none');
2656             return;
2657         }
2658         // FIXME - info.card == true!!!
2659         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2660         
2661         if (info.card !== true) {
2662             var cardel = info.card.el.dom;
2663             
2664             if (info.position == 'above') {
2665                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2666             } else if (cardel.nextSibling) {
2667                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2668             } else {
2669                 cardel.parentNode.append(this.dropEl.dom);
2670             }
2671         } else {
2672             // card container???
2673             this.containerEl.dom.append(this.dropEl.dom);
2674         }
2675         
2676         this.dropEl.addClass('d-block roo-card-dropzone');
2677         
2678         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2679         
2680         
2681     
2682     
2683     
2684     },
2685     setHeaderText: function(html)
2686     {
2687         this.header = html;
2688         if (this.headerContainerEl) {
2689             this.headerContainerEl.dom.innerHTML = html;
2690         }
2691     },
2692     onHeaderImageLoad : function(ev, he)
2693     {
2694         if (!this.header_image_fit_square) {
2695             return;
2696         }
2697         
2698         var hw = he.naturalHeight / he.naturalWidth;
2699         // wide image = < 0
2700         // tall image = > 1
2701         //var w = he.dom.naturalWidth;
2702         var ww = he.width;
2703         he.style.left =  0;
2704         he.style.position =  'relative';
2705         if (hw > 1) {
2706             var nw = (ww * (1/hw));
2707             Roo.get(he).setSize( ww * (1/hw),  ww);
2708             he.style.left =  ((ww - nw)/ 2) + 'px';
2709             he.style.position =  'relative';
2710         }
2711
2712     }
2713
2714     
2715 });
2716
2717 /*
2718  * - LGPL
2719  *
2720  * Card header - holder for the card header elements.
2721  * 
2722  */
2723
2724 /**
2725  * @class Roo.bootstrap.CardHeader
2726  * @extends Roo.bootstrap.Element
2727  * Bootstrap CardHeader class
2728  * @constructor
2729  * Create a new Card Header - that you can embed children into
2730  * @param {Object} config The config object
2731  */
2732
2733 Roo.bootstrap.CardHeader = function(config){
2734     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2735 };
2736
2737 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2738     
2739     
2740     container_method : 'getCardHeader' 
2741     
2742      
2743     
2744     
2745    
2746 });
2747
2748  
2749
2750  /*
2751  * - LGPL
2752  *
2753  * Card footer - holder for the card footer elements.
2754  * 
2755  */
2756
2757 /**
2758  * @class Roo.bootstrap.CardFooter
2759  * @extends Roo.bootstrap.Element
2760  * Bootstrap CardFooter class
2761  * @constructor
2762  * Create a new Card Footer - that you can embed children into
2763  * @param {Object} config The config object
2764  */
2765
2766 Roo.bootstrap.CardFooter = function(config){
2767     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2768 };
2769
2770 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2771     
2772     
2773     container_method : 'getCardFooter' 
2774     
2775      
2776     
2777     
2778    
2779 });
2780
2781  
2782
2783  /*
2784  * - LGPL
2785  *
2786  * Card header - holder for the card header elements.
2787  * 
2788  */
2789
2790 /**
2791  * @class Roo.bootstrap.CardImageTop
2792  * @extends Roo.bootstrap.Element
2793  * Bootstrap CardImageTop class
2794  * @constructor
2795  * Create a new Card Image Top container
2796  * @param {Object} config The config object
2797  */
2798
2799 Roo.bootstrap.CardImageTop = function(config){
2800     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2801 };
2802
2803 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2804     
2805    
2806     container_method : 'getCardImageTop' 
2807     
2808      
2809     
2810    
2811 });
2812
2813  
2814
2815  /*
2816  * - LGPL
2817  *
2818  * image
2819  * 
2820  */
2821
2822
2823 /**
2824  * @class Roo.bootstrap.Img
2825  * @extends Roo.bootstrap.Component
2826  * Bootstrap Img class
2827  * @cfg {Boolean} imgResponsive false | true
2828  * @cfg {String} border rounded | circle | thumbnail
2829  * @cfg {String} src image source
2830  * @cfg {String} alt image alternative text
2831  * @cfg {String} href a tag href
2832  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2833  * @cfg {String} xsUrl xs image source
2834  * @cfg {String} smUrl sm image source
2835  * @cfg {String} mdUrl md image source
2836  * @cfg {String} lgUrl lg image source
2837  * 
2838  * @constructor
2839  * Create a new Input
2840  * @param {Object} config The config object
2841  */
2842
2843 Roo.bootstrap.Img = function(config){
2844     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2845     
2846     this.addEvents({
2847         // img events
2848         /**
2849          * @event click
2850          * The img click event for the img.
2851          * @param {Roo.EventObject} e
2852          */
2853         "click" : true
2854     });
2855 };
2856
2857 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2858     
2859     imgResponsive: true,
2860     border: '',
2861     src: 'about:blank',
2862     href: false,
2863     target: false,
2864     xsUrl: '',
2865     smUrl: '',
2866     mdUrl: '',
2867     lgUrl: '',
2868
2869     getAutoCreate : function()
2870     {   
2871         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2872             return this.createSingleImg();
2873         }
2874         
2875         var cfg = {
2876             tag: 'div',
2877             cls: 'roo-image-responsive-group',
2878             cn: []
2879         };
2880         var _this = this;
2881         
2882         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2883             
2884             if(!_this[size + 'Url']){
2885                 return;
2886             }
2887             
2888             var img = {
2889                 tag: 'img',
2890                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2891                 html: _this.html || cfg.html,
2892                 src: _this[size + 'Url']
2893             };
2894             
2895             img.cls += ' roo-image-responsive-' + size;
2896             
2897             var s = ['xs', 'sm', 'md', 'lg'];
2898             
2899             s.splice(s.indexOf(size), 1);
2900             
2901             Roo.each(s, function(ss){
2902                 img.cls += ' hidden-' + ss;
2903             });
2904             
2905             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2906                 cfg.cls += ' img-' + _this.border;
2907             }
2908             
2909             if(_this.alt){
2910                 cfg.alt = _this.alt;
2911             }
2912             
2913             if(_this.href){
2914                 var a = {
2915                     tag: 'a',
2916                     href: _this.href,
2917                     cn: [
2918                         img
2919                     ]
2920                 };
2921
2922                 if(this.target){
2923                     a.target = _this.target;
2924                 }
2925             }
2926             
2927             cfg.cn.push((_this.href) ? a : img);
2928             
2929         });
2930         
2931         return cfg;
2932     },
2933     
2934     createSingleImg : function()
2935     {
2936         var cfg = {
2937             tag: 'img',
2938             cls: (this.imgResponsive) ? 'img-responsive' : '',
2939             html : null,
2940             src : 'about:blank'  // just incase src get's set to undefined?!?
2941         };
2942         
2943         cfg.html = this.html || cfg.html;
2944         
2945         cfg.src = this.src || cfg.src;
2946         
2947         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2948             cfg.cls += ' img-' + this.border;
2949         }
2950         
2951         if(this.alt){
2952             cfg.alt = this.alt;
2953         }
2954         
2955         if(this.href){
2956             var a = {
2957                 tag: 'a',
2958                 href: this.href,
2959                 cn: [
2960                     cfg
2961                 ]
2962             };
2963             
2964             if(this.target){
2965                 a.target = this.target;
2966             }
2967             
2968         }
2969         
2970         return (this.href) ? a : cfg;
2971     },
2972     
2973     initEvents: function() 
2974     {
2975         if(!this.href){
2976             this.el.on('click', this.onClick, this);
2977         }
2978         
2979     },
2980     
2981     onClick : function(e)
2982     {
2983         Roo.log('img onclick');
2984         this.fireEvent('click', this, e);
2985     },
2986     /**
2987      * Sets the url of the image - used to update it
2988      * @param {String} url the url of the image
2989      */
2990     
2991     setSrc : function(url)
2992     {
2993         this.src =  url;
2994         
2995         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2996             this.el.dom.src =  url;
2997             return;
2998         }
2999         
3000         this.el.select('img', true).first().dom.src =  url;
3001     }
3002     
3003     
3004    
3005 });
3006
3007  /*
3008  * - LGPL
3009  *
3010  * image
3011  * 
3012  */
3013
3014
3015 /**
3016  * @class Roo.bootstrap.Link
3017  * @extends Roo.bootstrap.Component
3018  * Bootstrap Link Class
3019  * @cfg {String} alt image alternative text
3020  * @cfg {String} href a tag href
3021  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3022  * @cfg {String} html the content of the link.
3023  * @cfg {String} anchor name for the anchor link
3024  * @cfg {String} fa - favicon
3025
3026  * @cfg {Boolean} preventDefault (true | false) default false
3027
3028  * 
3029  * @constructor
3030  * Create a new Input
3031  * @param {Object} config The config object
3032  */
3033
3034 Roo.bootstrap.Link = function(config){
3035     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3036     
3037     this.addEvents({
3038         // img events
3039         /**
3040          * @event click
3041          * The img click event for the img.
3042          * @param {Roo.EventObject} e
3043          */
3044         "click" : true
3045     });
3046 };
3047
3048 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3049     
3050     href: false,
3051     target: false,
3052     preventDefault: false,
3053     anchor : false,
3054     alt : false,
3055     fa: false,
3056
3057
3058     getAutoCreate : function()
3059     {
3060         var html = this.html || '';
3061         
3062         if (this.fa !== false) {
3063             html = '<i class="fa fa-' + this.fa + '"></i>';
3064         }
3065         var cfg = {
3066             tag: 'a'
3067         };
3068         // anchor's do not require html/href...
3069         if (this.anchor === false) {
3070             cfg.html = html;
3071             cfg.href = this.href || '#';
3072         } else {
3073             cfg.name = this.anchor;
3074             if (this.html !== false || this.fa !== false) {
3075                 cfg.html = html;
3076             }
3077             if (this.href !== false) {
3078                 cfg.href = this.href;
3079             }
3080         }
3081         
3082         if(this.alt !== false){
3083             cfg.alt = this.alt;
3084         }
3085         
3086         
3087         if(this.target !== false) {
3088             cfg.target = this.target;
3089         }
3090         
3091         return cfg;
3092     },
3093     
3094     initEvents: function() {
3095         
3096         if(!this.href || this.preventDefault){
3097             this.el.on('click', this.onClick, this);
3098         }
3099     },
3100     
3101     onClick : function(e)
3102     {
3103         if(this.preventDefault){
3104             e.preventDefault();
3105         }
3106         //Roo.log('img onclick');
3107         this.fireEvent('click', this, e);
3108     }
3109    
3110 });
3111
3112  /*
3113  * - LGPL
3114  *
3115  * header
3116  * 
3117  */
3118
3119 /**
3120  * @class Roo.bootstrap.Header
3121  * @extends Roo.bootstrap.Component
3122  * Bootstrap Header class
3123  * @cfg {String} html content of header
3124  * @cfg {Number} level (1|2|3|4|5|6) default 1
3125  * 
3126  * @constructor
3127  * Create a new Header
3128  * @param {Object} config The config object
3129  */
3130
3131
3132 Roo.bootstrap.Header  = function(config){
3133     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3134 };
3135
3136 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3137     
3138     //href : false,
3139     html : false,
3140     level : 1,
3141     
3142     
3143     
3144     getAutoCreate : function(){
3145         
3146         
3147         
3148         var cfg = {
3149             tag: 'h' + (1 *this.level),
3150             html: this.html || ''
3151         } ;
3152         
3153         return cfg;
3154     }
3155    
3156 });
3157
3158  
3159
3160  /*
3161  * Based on:
3162  * Ext JS Library 1.1.1
3163  * Copyright(c) 2006-2007, Ext JS, LLC.
3164  *
3165  * Originally Released Under LGPL - original licence link has changed is not relivant.
3166  *
3167  * Fork - LGPL
3168  * <script type="text/javascript">
3169  */
3170  
3171 /**
3172  * @class Roo.bootstrap.MenuMgr
3173  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3174  * @singleton
3175  */
3176 Roo.bootstrap.MenuMgr = function(){
3177    var menus, active, groups = {}, attached = false, lastShow = new Date();
3178
3179    // private - called when first menu is created
3180    function init(){
3181        menus = {};
3182        active = new Roo.util.MixedCollection();
3183        Roo.get(document).addKeyListener(27, function(){
3184            if(active.length > 0){
3185                hideAll();
3186            }
3187        });
3188    }
3189
3190    // private
3191    function hideAll(){
3192        if(active && active.length > 0){
3193            var c = active.clone();
3194            c.each(function(m){
3195                m.hide();
3196            });
3197        }
3198    }
3199
3200    // private
3201    function onHide(m){
3202        active.remove(m);
3203        if(active.length < 1){
3204            Roo.get(document).un("mouseup", onMouseDown);
3205             
3206            attached = false;
3207        }
3208    }
3209
3210    // private
3211    function onShow(m){
3212        var last = active.last();
3213        lastShow = new Date();
3214        active.add(m);
3215        if(!attached){
3216           Roo.get(document).on("mouseup", onMouseDown);
3217            
3218            attached = true;
3219        }
3220        if(m.parentMenu){
3221           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3222           m.parentMenu.activeChild = m;
3223        }else if(last && last.isVisible()){
3224           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3225        }
3226    }
3227
3228    // private
3229    function onBeforeHide(m){
3230        if(m.activeChild){
3231            m.activeChild.hide();
3232        }
3233        if(m.autoHideTimer){
3234            clearTimeout(m.autoHideTimer);
3235            delete m.autoHideTimer;
3236        }
3237    }
3238
3239    // private
3240    function onBeforeShow(m){
3241        var pm = m.parentMenu;
3242        if(!pm && !m.allowOtherMenus){
3243            hideAll();
3244        }else if(pm && pm.activeChild && active != m){
3245            pm.activeChild.hide();
3246        }
3247    }
3248
3249    // private this should really trigger on mouseup..
3250    function onMouseDown(e){
3251         Roo.log("on Mouse Up");
3252         
3253         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3254             Roo.log("MenuManager hideAll");
3255             hideAll();
3256             e.stopEvent();
3257         }
3258         
3259         
3260    }
3261
3262    // private
3263    function onBeforeCheck(mi, state){
3264        if(state){
3265            var g = groups[mi.group];
3266            for(var i = 0, l = g.length; i < l; i++){
3267                if(g[i] != mi){
3268                    g[i].setChecked(false);
3269                }
3270            }
3271        }
3272    }
3273
3274    return {
3275
3276        /**
3277         * Hides all menus that are currently visible
3278         */
3279        hideAll : function(){
3280             hideAll();  
3281        },
3282
3283        // private
3284        register : function(menu){
3285            if(!menus){
3286                init();
3287            }
3288            menus[menu.id] = menu;
3289            menu.on("beforehide", onBeforeHide);
3290            menu.on("hide", onHide);
3291            menu.on("beforeshow", onBeforeShow);
3292            menu.on("show", onShow);
3293            var g = menu.group;
3294            if(g && menu.events["checkchange"]){
3295                if(!groups[g]){
3296                    groups[g] = [];
3297                }
3298                groups[g].push(menu);
3299                menu.on("checkchange", onCheck);
3300            }
3301        },
3302
3303         /**
3304          * Returns a {@link Roo.menu.Menu} object
3305          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3306          * be used to generate and return a new Menu instance.
3307          */
3308        get : function(menu){
3309            if(typeof menu == "string"){ // menu id
3310                return menus[menu];
3311            }else if(menu.events){  // menu instance
3312                return menu;
3313            }
3314            /*else if(typeof menu.length == 'number'){ // array of menu items?
3315                return new Roo.bootstrap.Menu({items:menu});
3316            }else{ // otherwise, must be a config
3317                return new Roo.bootstrap.Menu(menu);
3318            }
3319            */
3320            return false;
3321        },
3322
3323        // private
3324        unregister : function(menu){
3325            delete menus[menu.id];
3326            menu.un("beforehide", onBeforeHide);
3327            menu.un("hide", onHide);
3328            menu.un("beforeshow", onBeforeShow);
3329            menu.un("show", onShow);
3330            var g = menu.group;
3331            if(g && menu.events["checkchange"]){
3332                groups[g].remove(menu);
3333                menu.un("checkchange", onCheck);
3334            }
3335        },
3336
3337        // private
3338        registerCheckable : function(menuItem){
3339            var g = menuItem.group;
3340            if(g){
3341                if(!groups[g]){
3342                    groups[g] = [];
3343                }
3344                groups[g].push(menuItem);
3345                menuItem.on("beforecheckchange", onBeforeCheck);
3346            }
3347        },
3348
3349        // private
3350        unregisterCheckable : function(menuItem){
3351            var g = menuItem.group;
3352            if(g){
3353                groups[g].remove(menuItem);
3354                menuItem.un("beforecheckchange", onBeforeCheck);
3355            }
3356        }
3357    };
3358 }();/*
3359  * - LGPL
3360  *
3361  * menu
3362  * 
3363  */
3364
3365 /**
3366  * @class Roo.bootstrap.Menu
3367  * @extends Roo.bootstrap.Component
3368  * Bootstrap Menu class - container for MenuItems
3369  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3370  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3371  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3372  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3373  * 
3374  * @constructor
3375  * Create a new Menu
3376  * @param {Object} config The config object
3377  */
3378
3379
3380 Roo.bootstrap.Menu = function(config){
3381     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3382     if (this.registerMenu && this.type != 'treeview')  {
3383         Roo.bootstrap.MenuMgr.register(this);
3384     }
3385     
3386     
3387     this.addEvents({
3388         /**
3389          * @event beforeshow
3390          * Fires before this menu is displayed (return false to block)
3391          * @param {Roo.menu.Menu} this
3392          */
3393         beforeshow : true,
3394         /**
3395          * @event beforehide
3396          * Fires before this menu is hidden (return false to block)
3397          * @param {Roo.menu.Menu} this
3398          */
3399         beforehide : true,
3400         /**
3401          * @event show
3402          * Fires after this menu is displayed
3403          * @param {Roo.menu.Menu} this
3404          */
3405         show : true,
3406         /**
3407          * @event hide
3408          * Fires after this menu is hidden
3409          * @param {Roo.menu.Menu} this
3410          */
3411         hide : true,
3412         /**
3413          * @event click
3414          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3415          * @param {Roo.menu.Menu} this
3416          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3417          * @param {Roo.EventObject} e
3418          */
3419         click : true,
3420         /**
3421          * @event mouseover
3422          * Fires when the mouse is hovering over this menu
3423          * @param {Roo.menu.Menu} this
3424          * @param {Roo.EventObject} e
3425          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3426          */
3427         mouseover : true,
3428         /**
3429          * @event mouseout
3430          * Fires when the mouse exits this menu
3431          * @param {Roo.menu.Menu} this
3432          * @param {Roo.EventObject} e
3433          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3434          */
3435         mouseout : true,
3436         /**
3437          * @event itemclick
3438          * Fires when a menu item contained in this menu is clicked
3439          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3440          * @param {Roo.EventObject} e
3441          */
3442         itemclick: true
3443     });
3444     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3445 };
3446
3447 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3448     
3449    /// html : false,
3450     //align : '',
3451     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3452     type: false,
3453     /**
3454      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3455      */
3456     registerMenu : true,
3457     
3458     menuItems :false, // stores the menu items..
3459     
3460     hidden:true,
3461         
3462     parentMenu : false,
3463     
3464     stopEvent : true,
3465     
3466     isLink : false,
3467     
3468     getChildContainer : function() {
3469         return this.el;  
3470     },
3471     
3472     getAutoCreate : function(){
3473          
3474         //if (['right'].indexOf(this.align)!==-1) {
3475         //    cfg.cn[1].cls += ' pull-right'
3476         //}
3477         
3478         
3479         var cfg = {
3480             tag : 'ul',
3481             cls : 'dropdown-menu' ,
3482             style : 'z-index:1000'
3483             
3484         };
3485         
3486         if (this.type === 'submenu') {
3487             cfg.cls = 'submenu active';
3488         }
3489         if (this.type === 'treeview') {
3490             cfg.cls = 'treeview-menu';
3491         }
3492         
3493         return cfg;
3494     },
3495     initEvents : function() {
3496         
3497        // Roo.log("ADD event");
3498        // Roo.log(this.triggerEl.dom);
3499         
3500         this.triggerEl.on('click', this.onTriggerClick, this);
3501         
3502         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3503         
3504         
3505         if (this.triggerEl.hasClass('nav-item')) {
3506             // dropdown toggle on the 'a' in BS4?
3507             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3508         } else {
3509             this.triggerEl.addClass('dropdown-toggle');
3510         }
3511         if (Roo.isTouch) {
3512             this.el.on('touchstart'  , this.onTouch, this);
3513         }
3514         this.el.on('click' , this.onClick, this);
3515
3516         this.el.on("mouseover", this.onMouseOver, this);
3517         this.el.on("mouseout", this.onMouseOut, this);
3518         
3519     },
3520     
3521     findTargetItem : function(e)
3522     {
3523         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3524         if(!t){
3525             return false;
3526         }
3527         //Roo.log(t);         Roo.log(t.id);
3528         if(t && t.id){
3529             //Roo.log(this.menuitems);
3530             return this.menuitems.get(t.id);
3531             
3532             //return this.items.get(t.menuItemId);
3533         }
3534         
3535         return false;
3536     },
3537     
3538     onTouch : function(e) 
3539     {
3540         Roo.log("menu.onTouch");
3541         //e.stopEvent(); this make the user popdown broken
3542         this.onClick(e);
3543     },
3544     
3545     onClick : function(e)
3546     {
3547         Roo.log("menu.onClick");
3548         
3549         var t = this.findTargetItem(e);
3550         if(!t || t.isContainer){
3551             return;
3552         }
3553         Roo.log(e);
3554         /*
3555         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3556             if(t == this.activeItem && t.shouldDeactivate(e)){
3557                 this.activeItem.deactivate();
3558                 delete this.activeItem;
3559                 return;
3560             }
3561             if(t.canActivate){
3562                 this.setActiveItem(t, true);
3563             }
3564             return;
3565             
3566             
3567         }
3568         */
3569        
3570         Roo.log('pass click event');
3571         
3572         t.onClick(e);
3573         
3574         this.fireEvent("click", this, t, e);
3575         
3576         var _this = this;
3577         
3578         if(!t.href.length || t.href == '#'){
3579             (function() { _this.hide(); }).defer(100);
3580         }
3581         
3582     },
3583     
3584     onMouseOver : function(e){
3585         var t  = this.findTargetItem(e);
3586         //Roo.log(t);
3587         //if(t){
3588         //    if(t.canActivate && !t.disabled){
3589         //        this.setActiveItem(t, true);
3590         //    }
3591         //}
3592         
3593         this.fireEvent("mouseover", this, e, t);
3594     },
3595     isVisible : function(){
3596         return !this.hidden;
3597     },
3598     onMouseOut : function(e){
3599         var t  = this.findTargetItem(e);
3600         
3601         //if(t ){
3602         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3603         //        this.activeItem.deactivate();
3604         //        delete this.activeItem;
3605         //    }
3606         //}
3607         this.fireEvent("mouseout", this, e, t);
3608     },
3609     
3610     
3611     /**
3612      * Displays this menu relative to another element
3613      * @param {String/HTMLElement/Roo.Element} element The element to align to
3614      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3615      * the element (defaults to this.defaultAlign)
3616      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3617      */
3618     show : function(el, pos, parentMenu)
3619     {
3620         if (false === this.fireEvent("beforeshow", this)) {
3621             Roo.log("show canceled");
3622             return;
3623         }
3624         this.parentMenu = parentMenu;
3625         if(!this.el){
3626             this.render();
3627         }
3628         
3629         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3630     },
3631      /**
3632      * Displays this menu at a specific xy position
3633      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3634      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3635      */
3636     showAt : function(xy, parentMenu, /* private: */_e){
3637         this.parentMenu = parentMenu;
3638         if(!this.el){
3639             this.render();
3640         }
3641         if(_e !== false){
3642             this.fireEvent("beforeshow", this);
3643             //xy = this.el.adjustForConstraints(xy);
3644         }
3645         
3646         //this.el.show();
3647         this.hideMenuItems();
3648         this.hidden = false;
3649         this.triggerEl.addClass('open');
3650         this.el.addClass('show');
3651         
3652         // reassign x when hitting right
3653         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3654             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3655         }
3656         
3657         // reassign y when hitting bottom
3658         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3659             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3660         }
3661         
3662         // but the list may align on trigger left or trigger top... should it be a properity?
3663         
3664         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3665             this.el.setXY(xy);
3666         }
3667         
3668         this.focus();
3669         this.fireEvent("show", this);
3670     },
3671     
3672     focus : function(){
3673         return;
3674         if(!this.hidden){
3675             this.doFocus.defer(50, this);
3676         }
3677     },
3678
3679     doFocus : function(){
3680         if(!this.hidden){
3681             this.focusEl.focus();
3682         }
3683     },
3684
3685     /**
3686      * Hides this menu and optionally all parent menus
3687      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3688      */
3689     hide : function(deep)
3690     {
3691         if (false === this.fireEvent("beforehide", this)) {
3692             Roo.log("hide canceled");
3693             return;
3694         }
3695         this.hideMenuItems();
3696         if(this.el && this.isVisible()){
3697            
3698             if(this.activeItem){
3699                 this.activeItem.deactivate();
3700                 this.activeItem = null;
3701             }
3702             this.triggerEl.removeClass('open');;
3703             this.el.removeClass('show');
3704             this.hidden = true;
3705             this.fireEvent("hide", this);
3706         }
3707         if(deep === true && this.parentMenu){
3708             this.parentMenu.hide(true);
3709         }
3710     },
3711     
3712     onTriggerClick : function(e)
3713     {
3714         Roo.log('trigger click');
3715         
3716         var target = e.getTarget();
3717         
3718         Roo.log(target.nodeName.toLowerCase());
3719         
3720         if(target.nodeName.toLowerCase() === 'i'){
3721             e.preventDefault();
3722         }
3723         
3724     },
3725     
3726     onTriggerPress  : function(e)
3727     {
3728         Roo.log('trigger press');
3729         //Roo.log(e.getTarget());
3730        // Roo.log(this.triggerEl.dom);
3731        
3732         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3733         var pel = Roo.get(e.getTarget());
3734         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3735             Roo.log('is treeview or dropdown?');
3736             return;
3737         }
3738         
3739         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3740             return;
3741         }
3742         
3743         if (this.isVisible()) {
3744             Roo.log('hide');
3745             this.hide();
3746         } else {
3747             Roo.log('show');
3748             this.show(this.triggerEl, '?', false);
3749         }
3750         
3751         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3752             e.stopEvent();
3753         }
3754         
3755     },
3756        
3757     
3758     hideMenuItems : function()
3759     {
3760         Roo.log("hide Menu Items");
3761         if (!this.el) { 
3762             return;
3763         }
3764         
3765         this.el.select('.open',true).each(function(aa) {
3766             
3767             aa.removeClass('open');
3768          
3769         });
3770     },
3771     addxtypeChild : function (tree, cntr) {
3772         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3773           
3774         this.menuitems.add(comp);
3775         return comp;
3776
3777     },
3778     getEl : function()
3779     {
3780         Roo.log(this.el);
3781         return this.el;
3782     },
3783     
3784     clear : function()
3785     {
3786         this.getEl().dom.innerHTML = '';
3787         this.menuitems.clear();
3788     }
3789 });
3790
3791  
3792  /*
3793  * - LGPL
3794  *
3795  * menu item
3796  * 
3797  */
3798
3799
3800 /**
3801  * @class Roo.bootstrap.MenuItem
3802  * @extends Roo.bootstrap.Component
3803  * Bootstrap MenuItem class
3804  * @cfg {String} html the menu label
3805  * @cfg {String} href the link
3806  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3807  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3808  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3809  * @cfg {String} fa favicon to show on left of menu item.
3810  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3811  * 
3812  * 
3813  * @constructor
3814  * Create a new MenuItem
3815  * @param {Object} config The config object
3816  */
3817
3818
3819 Roo.bootstrap.MenuItem = function(config){
3820     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3821     this.addEvents({
3822         // raw events
3823         /**
3824          * @event click
3825          * The raw click event for the entire grid.
3826          * @param {Roo.bootstrap.MenuItem} this
3827          * @param {Roo.EventObject} e
3828          */
3829         "click" : true
3830     });
3831 };
3832
3833 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3834     
3835     href : false,
3836     html : false,
3837     preventDefault: false,
3838     isContainer : false,
3839     active : false,
3840     fa: false,
3841     
3842     getAutoCreate : function(){
3843         
3844         if(this.isContainer){
3845             return {
3846                 tag: 'li',
3847                 cls: 'dropdown-menu-item '
3848             };
3849         }
3850         var ctag = {
3851             tag: 'span',
3852             html: 'Link'
3853         };
3854         
3855         var anc = {
3856             tag : 'a',
3857             cls : 'dropdown-item',
3858             href : '#',
3859             cn : [  ]
3860         };
3861         
3862         if (this.fa !== false) {
3863             anc.cn.push({
3864                 tag : 'i',
3865                 cls : 'fa fa-' + this.fa
3866             });
3867         }
3868         
3869         anc.cn.push(ctag);
3870         
3871         
3872         var cfg= {
3873             tag: 'li',
3874             cls: 'dropdown-menu-item',
3875             cn: [ anc ]
3876         };
3877         if (this.parent().type == 'treeview') {
3878             cfg.cls = 'treeview-menu';
3879         }
3880         if (this.active) {
3881             cfg.cls += ' active';
3882         }
3883         
3884         
3885         
3886         anc.href = this.href || cfg.cn[0].href ;
3887         ctag.html = this.html || cfg.cn[0].html ;
3888         return cfg;
3889     },
3890     
3891     initEvents: function()
3892     {
3893         if (this.parent().type == 'treeview') {
3894             this.el.select('a').on('click', this.onClick, this);
3895         }
3896         
3897         if (this.menu) {
3898             this.menu.parentType = this.xtype;
3899             this.menu.triggerEl = this.el;
3900             this.menu = this.addxtype(Roo.apply({}, this.menu));
3901         }
3902         
3903     },
3904     onClick : function(e)
3905     {
3906         Roo.log('item on click ');
3907         
3908         if(this.preventDefault){
3909             e.preventDefault();
3910         }
3911         //this.parent().hideMenuItems();
3912         
3913         this.fireEvent('click', this, e);
3914     },
3915     getEl : function()
3916     {
3917         return this.el;
3918     } 
3919 });
3920
3921  
3922
3923  /*
3924  * - LGPL
3925  *
3926  * menu separator
3927  * 
3928  */
3929
3930
3931 /**
3932  * @class Roo.bootstrap.MenuSeparator
3933  * @extends Roo.bootstrap.Component
3934  * Bootstrap MenuSeparator class
3935  * 
3936  * @constructor
3937  * Create a new MenuItem
3938  * @param {Object} config The config object
3939  */
3940
3941
3942 Roo.bootstrap.MenuSeparator = function(config){
3943     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3944 };
3945
3946 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3947     
3948     getAutoCreate : function(){
3949         var cfg = {
3950             cls: 'divider',
3951             tag : 'li'
3952         };
3953         
3954         return cfg;
3955     }
3956    
3957 });
3958
3959  
3960
3961  
3962 /*
3963 * Licence: LGPL
3964 */
3965
3966 /**
3967  * @class Roo.bootstrap.Modal
3968  * @extends Roo.bootstrap.Component
3969  * Bootstrap Modal class
3970  * @cfg {String} title Title of dialog
3971  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3972  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3973  * @cfg {Boolean} specificTitle default false
3974  * @cfg {Array} buttons Array of buttons or standard button set..
3975  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3976  * @cfg {Boolean} animate default true
3977  * @cfg {Boolean} allow_close default true
3978  * @cfg {Boolean} fitwindow default false
3979  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3980  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3981  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3982  * @cfg {String} size (sm|lg|xl) default empty
3983  * @cfg {Number} max_width set the max width of modal
3984  * @cfg {Boolean} editableTitle can the title be edited
3985
3986  *
3987  *
3988  * @constructor
3989  * Create a new Modal Dialog
3990  * @param {Object} config The config object
3991  */
3992
3993 Roo.bootstrap.Modal = function(config){
3994     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3995     this.addEvents({
3996         // raw events
3997         /**
3998          * @event btnclick
3999          * The raw btnclick event for the button
4000          * @param {Roo.EventObject} e
4001          */
4002         "btnclick" : true,
4003         /**
4004          * @event resize
4005          * Fire when dialog resize
4006          * @param {Roo.bootstrap.Modal} this
4007          * @param {Roo.EventObject} e
4008          */
4009         "resize" : true,
4010         /**
4011          * @event titlechanged
4012          * Fire when the editable title has been changed
4013          * @param {Roo.bootstrap.Modal} this
4014          * @param {Roo.EventObject} value
4015          */
4016         "titlechanged" : true 
4017         
4018     });
4019     this.buttons = this.buttons || [];
4020
4021     if (this.tmpl) {
4022         this.tmpl = Roo.factory(this.tmpl);
4023     }
4024
4025 };
4026
4027 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4028
4029     title : 'test dialog',
4030
4031     buttons : false,
4032
4033     // set on load...
4034
4035     html: false,
4036
4037     tmp: false,
4038
4039     specificTitle: false,
4040
4041     buttonPosition: 'right',
4042
4043     allow_close : true,
4044
4045     animate : true,
4046
4047     fitwindow: false,
4048     
4049      // private
4050     dialogEl: false,
4051     bodyEl:  false,
4052     footerEl:  false,
4053     titleEl:  false,
4054     closeEl:  false,
4055
4056     size: '',
4057     
4058     max_width: 0,
4059     
4060     max_height: 0,
4061     
4062     fit_content: false,
4063     editableTitle  : false,
4064
4065     onRender : function(ct, position)
4066     {
4067         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4068
4069         if(!this.el){
4070             var cfg = Roo.apply({},  this.getAutoCreate());
4071             cfg.id = Roo.id();
4072             //if(!cfg.name){
4073             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4074             //}
4075             //if (!cfg.name.length) {
4076             //    delete cfg.name;
4077            // }
4078             if (this.cls) {
4079                 cfg.cls += ' ' + this.cls;
4080             }
4081             if (this.style) {
4082                 cfg.style = this.style;
4083             }
4084             this.el = Roo.get(document.body).createChild(cfg, position);
4085         }
4086         //var type = this.el.dom.type;
4087
4088
4089         if(this.tabIndex !== undefined){
4090             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4091         }
4092
4093         this.dialogEl = this.el.select('.modal-dialog',true).first();
4094         this.bodyEl = this.el.select('.modal-body',true).first();
4095         this.closeEl = this.el.select('.modal-header .close', true).first();
4096         this.headerEl = this.el.select('.modal-header',true).first();
4097         this.titleEl = this.el.select('.modal-title',true).first();
4098         this.footerEl = this.el.select('.modal-footer',true).first();
4099
4100         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4101         
4102         //this.el.addClass("x-dlg-modal");
4103
4104         if (this.buttons.length) {
4105             Roo.each(this.buttons, function(bb) {
4106                 var b = Roo.apply({}, bb);
4107                 b.xns = b.xns || Roo.bootstrap;
4108                 b.xtype = b.xtype || 'Button';
4109                 if (typeof(b.listeners) == 'undefined') {
4110                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4111                 }
4112
4113                 var btn = Roo.factory(b);
4114
4115                 btn.render(this.getButtonContainer());
4116
4117             },this);
4118         }
4119         // render the children.
4120         var nitems = [];
4121
4122         if(typeof(this.items) != 'undefined'){
4123             var items = this.items;
4124             delete this.items;
4125
4126             for(var i =0;i < items.length;i++) {
4127                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4128             }
4129         }
4130
4131         this.items = nitems;
4132
4133         // where are these used - they used to be body/close/footer
4134
4135
4136         this.initEvents();
4137         //this.el.addClass([this.fieldClass, this.cls]);
4138
4139     },
4140
4141     getAutoCreate : function()
4142     {
4143         // we will default to modal-body-overflow - might need to remove or make optional later.
4144         var bdy = {
4145                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4146                 html : this.html || ''
4147         };
4148
4149         var title = {
4150             tag: 'h5',
4151             cls : 'modal-title',
4152             html : this.title
4153         };
4154
4155         if(this.specificTitle){ // WTF is this?
4156             title = this.title;
4157         }
4158
4159         var header = [];
4160         if (this.allow_close && Roo.bootstrap.version == 3) {
4161             header.push({
4162                 tag: 'button',
4163                 cls : 'close',
4164                 html : '&times'
4165             });
4166         }
4167
4168         header.push(title);
4169
4170         if (this.editableTitle) {
4171             header.push({
4172                 cls: 'form-control roo-editable-title d-none',
4173                 tag: 'input',
4174                 type: 'text'
4175             });
4176         }
4177         
4178         if (this.allow_close && Roo.bootstrap.version == 4) {
4179             header.push({
4180                 tag: 'button',
4181                 cls : 'close',
4182                 html : '&times'
4183             });
4184         }
4185         
4186         var size = '';
4187
4188         if(this.size.length){
4189             size = 'modal-' + this.size;
4190         }
4191         
4192         var footer = Roo.bootstrap.version == 3 ?
4193             {
4194                 cls : 'modal-footer',
4195                 cn : [
4196                     {
4197                         tag: 'div',
4198                         cls: 'btn-' + this.buttonPosition
4199                     }
4200                 ]
4201
4202             } :
4203             {  // BS4 uses mr-auto on left buttons....
4204                 cls : 'modal-footer'
4205             };
4206
4207             
4208
4209         
4210         
4211         var modal = {
4212             cls: "modal",
4213              cn : [
4214                 {
4215                     cls: "modal-dialog " + size,
4216                     cn : [
4217                         {
4218                             cls : "modal-content",
4219                             cn : [
4220                                 {
4221                                     cls : 'modal-header',
4222                                     cn : header
4223                                 },
4224                                 bdy,
4225                                 footer
4226                             ]
4227
4228                         }
4229                     ]
4230
4231                 }
4232             ]
4233         };
4234
4235         if(this.animate){
4236             modal.cls += ' fade';
4237         }
4238
4239         return modal;
4240
4241     },
4242     getChildContainer : function() {
4243
4244          return this.bodyEl;
4245
4246     },
4247     getButtonContainer : function() {
4248         
4249          return Roo.bootstrap.version == 4 ?
4250             this.el.select('.modal-footer',true).first()
4251             : this.el.select('.modal-footer div',true).first();
4252
4253     },
4254     initEvents : function()
4255     {
4256         if (this.allow_close) {
4257             this.closeEl.on('click', this.hide, this);
4258         }
4259         Roo.EventManager.onWindowResize(this.resize, this, true);
4260         if (this.editableTitle) {
4261             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4262             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4263             this.headerEditEl.on('keyup', function(e) {
4264                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4265                         this.toggleHeaderInput(false)
4266                     }
4267                 }, this);
4268             this.headerEditEl.on('blur', function(e) {
4269                 this.toggleHeaderInput(false)
4270             },this);
4271         }
4272
4273     },
4274   
4275
4276     resize : function()
4277     {
4278         this.maskEl.setSize(
4279             Roo.lib.Dom.getViewWidth(true),
4280             Roo.lib.Dom.getViewHeight(true)
4281         );
4282         
4283         if (this.fitwindow) {
4284             
4285            this.dialogEl.setStyle( { 'max-width' : '100%' });
4286             this.setSize(
4287                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4288                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4289             );
4290             return;
4291         }
4292         
4293         if(this.max_width !== 0) {
4294             
4295             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4296             
4297             if(this.height) {
4298                 this.setSize(w, this.height);
4299                 return;
4300             }
4301             
4302             if(this.max_height) {
4303                 this.setSize(w,Math.min(
4304                     this.max_height,
4305                     Roo.lib.Dom.getViewportHeight(true) - 60
4306                 ));
4307                 
4308                 return;
4309             }
4310             
4311             if(!this.fit_content) {
4312                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4313                 return;
4314             }
4315             
4316             this.setSize(w, Math.min(
4317                 60 +
4318                 this.headerEl.getHeight() + 
4319                 this.footerEl.getHeight() + 
4320                 this.getChildHeight(this.bodyEl.dom.childNodes),
4321                 Roo.lib.Dom.getViewportHeight(true) - 60)
4322             );
4323         }
4324         
4325     },
4326
4327     setSize : function(w,h)
4328     {
4329         if (!w && !h) {
4330             return;
4331         }
4332         
4333         this.resizeTo(w,h);
4334     },
4335
4336     show : function() {
4337
4338         if (!this.rendered) {
4339             this.render();
4340         }
4341         this.toggleHeaderInput(false);
4342         //this.el.setStyle('display', 'block');
4343         this.el.removeClass('hideing');
4344         this.el.dom.style.display='block';
4345         
4346         Roo.get(document.body).addClass('modal-open');
4347  
4348         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4349             
4350             (function(){
4351                 this.el.addClass('show');
4352                 this.el.addClass('in');
4353             }).defer(50, this);
4354         }else{
4355             this.el.addClass('show');
4356             this.el.addClass('in');
4357         }
4358
4359         // not sure how we can show data in here..
4360         //if (this.tmpl) {
4361         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4362         //}
4363
4364         Roo.get(document.body).addClass("x-body-masked");
4365         
4366         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4367         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4368         this.maskEl.dom.style.display = 'block';
4369         this.maskEl.addClass('show');
4370         
4371         
4372         this.resize();
4373         
4374         this.fireEvent('show', this);
4375
4376         // set zindex here - otherwise it appears to be ignored...
4377         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4378
4379         (function () {
4380             this.items.forEach( function(e) {
4381                 e.layout ? e.layout() : false;
4382
4383             });
4384         }).defer(100,this);
4385
4386     },
4387     hide : function()
4388     {
4389         if(this.fireEvent("beforehide", this) !== false){
4390             
4391             this.maskEl.removeClass('show');
4392             
4393             this.maskEl.dom.style.display = '';
4394             Roo.get(document.body).removeClass("x-body-masked");
4395             this.el.removeClass('in');
4396             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4397
4398             if(this.animate){ // why
4399                 this.el.addClass('hideing');
4400                 this.el.removeClass('show');
4401                 (function(){
4402                     if (!this.el.hasClass('hideing')) {
4403                         return; // it's been shown again...
4404                     }
4405                     
4406                     this.el.dom.style.display='';
4407
4408                     Roo.get(document.body).removeClass('modal-open');
4409                     this.el.removeClass('hideing');
4410                 }).defer(150,this);
4411                 
4412             }else{
4413                 this.el.removeClass('show');
4414                 this.el.dom.style.display='';
4415                 Roo.get(document.body).removeClass('modal-open');
4416
4417             }
4418             this.fireEvent('hide', this);
4419         }
4420     },
4421     isVisible : function()
4422     {
4423         
4424         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4425         
4426     },
4427
4428     addButton : function(str, cb)
4429     {
4430
4431
4432         var b = Roo.apply({}, { html : str } );
4433         b.xns = b.xns || Roo.bootstrap;
4434         b.xtype = b.xtype || 'Button';
4435         if (typeof(b.listeners) == 'undefined') {
4436             b.listeners = { click : cb.createDelegate(this)  };
4437         }
4438
4439         var btn = Roo.factory(b);
4440
4441         btn.render(this.getButtonContainer());
4442
4443         return btn;
4444
4445     },
4446
4447     setDefaultButton : function(btn)
4448     {
4449         //this.el.select('.modal-footer').()
4450     },
4451
4452     resizeTo: function(w,h)
4453     {
4454         this.dialogEl.setWidth(w);
4455         
4456         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4457
4458         this.bodyEl.setHeight(h - diff);
4459         
4460         this.fireEvent('resize', this);
4461     },
4462     
4463     setContentSize  : function(w, h)
4464     {
4465
4466     },
4467     onButtonClick: function(btn,e)
4468     {
4469         //Roo.log([a,b,c]);
4470         this.fireEvent('btnclick', btn.name, e);
4471     },
4472      /**
4473      * Set the title of the Dialog
4474      * @param {String} str new Title
4475      */
4476     setTitle: function(str) {
4477         this.titleEl.dom.innerHTML = str;
4478         this.title = str;
4479     },
4480     /**
4481      * Set the body of the Dialog
4482      * @param {String} str new Title
4483      */
4484     setBody: function(str) {
4485         this.bodyEl.dom.innerHTML = str;
4486     },
4487     /**
4488      * Set the body of the Dialog using the template
4489      * @param {Obj} data - apply this data to the template and replace the body contents.
4490      */
4491     applyBody: function(obj)
4492     {
4493         if (!this.tmpl) {
4494             Roo.log("Error - using apply Body without a template");
4495             //code
4496         }
4497         this.tmpl.overwrite(this.bodyEl, obj);
4498     },
4499     
4500     getChildHeight : function(child_nodes)
4501     {
4502         if(
4503             !child_nodes ||
4504             child_nodes.length == 0
4505         ) {
4506             return 0;
4507         }
4508         
4509         var child_height = 0;
4510         
4511         for(var i = 0; i < child_nodes.length; i++) {
4512             
4513             /*
4514             * for modal with tabs...
4515             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4516                 
4517                 var layout_childs = child_nodes[i].childNodes;
4518                 
4519                 for(var j = 0; j < layout_childs.length; j++) {
4520                     
4521                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4522                         
4523                         var layout_body_childs = layout_childs[j].childNodes;
4524                         
4525                         for(var k = 0; k < layout_body_childs.length; k++) {
4526                             
4527                             if(layout_body_childs[k].classList.contains('navbar')) {
4528                                 child_height += layout_body_childs[k].offsetHeight;
4529                                 continue;
4530                             }
4531                             
4532                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4533                                 
4534                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4535                                 
4536                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4537                                     
4538                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4539                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4540                                         continue;
4541                                     }
4542                                     
4543                                 }
4544                                 
4545                             }
4546                             
4547                         }
4548                     }
4549                 }
4550                 continue;
4551             }
4552             */
4553             
4554             child_height += child_nodes[i].offsetHeight;
4555             // Roo.log(child_nodes[i].offsetHeight);
4556         }
4557         
4558         return child_height;
4559     },
4560     toggleHeaderInput : function(is_edit)
4561     {
4562         if (!this.editableTitle) {
4563             return; // not editable.
4564         }
4565         if (is_edit && this.is_header_editing) {
4566             return; // already editing..
4567         }
4568         if (is_edit) {
4569     
4570             this.headerEditEl.dom.value = this.title;
4571             this.headerEditEl.removeClass('d-none');
4572             this.headerEditEl.dom.focus();
4573             this.titleEl.addClass('d-none');
4574             
4575             this.is_header_editing = true;
4576             return
4577         }
4578         // flip back to not editing.
4579         this.title = this.headerEditEl.dom.value;
4580         this.headerEditEl.addClass('d-none');
4581         this.titleEl.removeClass('d-none');
4582         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4583         this.is_header_editing = false;
4584         this.fireEvent('titlechanged', this, this.title);
4585     
4586             
4587         
4588     }
4589
4590 });
4591
4592
4593 Roo.apply(Roo.bootstrap.Modal,  {
4594     /**
4595          * Button config that displays a single OK button
4596          * @type Object
4597          */
4598         OK :  [{
4599             name : 'ok',
4600             weight : 'primary',
4601             html : 'OK'
4602         }],
4603         /**
4604          * Button config that displays Yes and No buttons
4605          * @type Object
4606          */
4607         YESNO : [
4608             {
4609                 name  : 'no',
4610                 html : 'No'
4611             },
4612             {
4613                 name  :'yes',
4614                 weight : 'primary',
4615                 html : 'Yes'
4616             }
4617         ],
4618
4619         /**
4620          * Button config that displays OK and Cancel buttons
4621          * @type Object
4622          */
4623         OKCANCEL : [
4624             {
4625                name : 'cancel',
4626                 html : 'Cancel'
4627             },
4628             {
4629                 name : 'ok',
4630                 weight : 'primary',
4631                 html : 'OK'
4632             }
4633         ],
4634         /**
4635          * Button config that displays Yes, No and Cancel buttons
4636          * @type Object
4637          */
4638         YESNOCANCEL : [
4639             {
4640                 name : 'yes',
4641                 weight : 'primary',
4642                 html : 'Yes'
4643             },
4644             {
4645                 name : 'no',
4646                 html : 'No'
4647             },
4648             {
4649                 name : 'cancel',
4650                 html : 'Cancel'
4651             }
4652         ],
4653         
4654         zIndex : 10001
4655 });
4656
4657 /*
4658  * - LGPL
4659  *
4660  * messagebox - can be used as a replace
4661  * 
4662  */
4663 /**
4664  * @class Roo.MessageBox
4665  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4666  * Example usage:
4667  *<pre><code>
4668 // Basic alert:
4669 Roo.Msg.alert('Status', 'Changes saved successfully.');
4670
4671 // Prompt for user data:
4672 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4673     if (btn == 'ok'){
4674         // process text value...
4675     }
4676 });
4677
4678 // Show a dialog using config options:
4679 Roo.Msg.show({
4680    title:'Save Changes?',
4681    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4682    buttons: Roo.Msg.YESNOCANCEL,
4683    fn: processResult,
4684    animEl: 'elId'
4685 });
4686 </code></pre>
4687  * @singleton
4688  */
4689 Roo.bootstrap.MessageBox = function(){
4690     var dlg, opt, mask, waitTimer;
4691     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4692     var buttons, activeTextEl, bwidth;
4693
4694     
4695     // private
4696     var handleButton = function(button){
4697         dlg.hide();
4698         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4699     };
4700
4701     // private
4702     var handleHide = function(){
4703         if(opt && opt.cls){
4704             dlg.el.removeClass(opt.cls);
4705         }
4706         //if(waitTimer){
4707         //    Roo.TaskMgr.stop(waitTimer);
4708         //    waitTimer = null;
4709         //}
4710     };
4711
4712     // private
4713     var updateButtons = function(b){
4714         var width = 0;
4715         if(!b){
4716             buttons["ok"].hide();
4717             buttons["cancel"].hide();
4718             buttons["yes"].hide();
4719             buttons["no"].hide();
4720             dlg.footerEl.hide();
4721             
4722             return width;
4723         }
4724         dlg.footerEl.show();
4725         for(var k in buttons){
4726             if(typeof buttons[k] != "function"){
4727                 if(b[k]){
4728                     buttons[k].show();
4729                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4730                     width += buttons[k].el.getWidth()+15;
4731                 }else{
4732                     buttons[k].hide();
4733                 }
4734             }
4735         }
4736         return width;
4737     };
4738
4739     // private
4740     var handleEsc = function(d, k, e){
4741         if(opt && opt.closable !== false){
4742             dlg.hide();
4743         }
4744         if(e){
4745             e.stopEvent();
4746         }
4747     };
4748
4749     return {
4750         /**
4751          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4752          * @return {Roo.BasicDialog} The BasicDialog element
4753          */
4754         getDialog : function(){
4755            if(!dlg){
4756                 dlg = new Roo.bootstrap.Modal( {
4757                     //draggable: true,
4758                     //resizable:false,
4759                     //constraintoviewport:false,
4760                     //fixedcenter:true,
4761                     //collapsible : false,
4762                     //shim:true,
4763                     //modal: true,
4764                 //    width: 'auto',
4765                   //  height:100,
4766                     //buttonAlign:"center",
4767                     closeClick : function(){
4768                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4769                             handleButton("no");
4770                         }else{
4771                             handleButton("cancel");
4772                         }
4773                     }
4774                 });
4775                 dlg.render();
4776                 dlg.on("hide", handleHide);
4777                 mask = dlg.mask;
4778                 //dlg.addKeyListener(27, handleEsc);
4779                 buttons = {};
4780                 this.buttons = buttons;
4781                 var bt = this.buttonText;
4782                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4783                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4784                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4785                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4786                 //Roo.log(buttons);
4787                 bodyEl = dlg.bodyEl.createChild({
4788
4789                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4790                         '<textarea class="roo-mb-textarea"></textarea>' +
4791                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4792                 });
4793                 msgEl = bodyEl.dom.firstChild;
4794                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4795                 textboxEl.enableDisplayMode();
4796                 textboxEl.addKeyListener([10,13], function(){
4797                     if(dlg.isVisible() && opt && opt.buttons){
4798                         if(opt.buttons.ok){
4799                             handleButton("ok");
4800                         }else if(opt.buttons.yes){
4801                             handleButton("yes");
4802                         }
4803                     }
4804                 });
4805                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4806                 textareaEl.enableDisplayMode();
4807                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4808                 progressEl.enableDisplayMode();
4809                 
4810                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4811                 var pf = progressEl.dom.firstChild;
4812                 if (pf) {
4813                     pp = Roo.get(pf.firstChild);
4814                     pp.setHeight(pf.offsetHeight);
4815                 }
4816                 
4817             }
4818             return dlg;
4819         },
4820
4821         /**
4822          * Updates the message box body text
4823          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4824          * the XHTML-compliant non-breaking space character '&amp;#160;')
4825          * @return {Roo.MessageBox} This message box
4826          */
4827         updateText : function(text)
4828         {
4829             if(!dlg.isVisible() && !opt.width){
4830                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4831                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4832             }
4833             msgEl.innerHTML = text || '&#160;';
4834       
4835             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4836             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4837             var w = Math.max(
4838                     Math.min(opt.width || cw , this.maxWidth), 
4839                     Math.max(opt.minWidth || this.minWidth, bwidth)
4840             );
4841             if(opt.prompt){
4842                 activeTextEl.setWidth(w);
4843             }
4844             if(dlg.isVisible()){
4845                 dlg.fixedcenter = false;
4846             }
4847             // to big, make it scroll. = But as usual stupid IE does not support
4848             // !important..
4849             
4850             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4851                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4852                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4853             } else {
4854                 bodyEl.dom.style.height = '';
4855                 bodyEl.dom.style.overflowY = '';
4856             }
4857             if (cw > w) {
4858                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4859             } else {
4860                 bodyEl.dom.style.overflowX = '';
4861             }
4862             
4863             dlg.setContentSize(w, bodyEl.getHeight());
4864             if(dlg.isVisible()){
4865                 dlg.fixedcenter = true;
4866             }
4867             return this;
4868         },
4869
4870         /**
4871          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4872          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4873          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4874          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4875          * @return {Roo.MessageBox} This message box
4876          */
4877         updateProgress : function(value, text){
4878             if(text){
4879                 this.updateText(text);
4880             }
4881             
4882             if (pp) { // weird bug on my firefox - for some reason this is not defined
4883                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4884                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4885             }
4886             return this;
4887         },        
4888
4889         /**
4890          * Returns true if the message box is currently displayed
4891          * @return {Boolean} True if the message box is visible, else false
4892          */
4893         isVisible : function(){
4894             return dlg && dlg.isVisible();  
4895         },
4896
4897         /**
4898          * Hides the message box if it is displayed
4899          */
4900         hide : function(){
4901             if(this.isVisible()){
4902                 dlg.hide();
4903             }  
4904         },
4905
4906         /**
4907          * Displays a new message box, or reinitializes an existing message box, based on the config options
4908          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4909          * The following config object properties are supported:
4910          * <pre>
4911 Property    Type             Description
4912 ----------  ---------------  ------------------------------------------------------------------------------------
4913 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4914                                    closes (defaults to undefined)
4915 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4916                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4917 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4918                                    progress and wait dialogs will ignore this property and always hide the
4919                                    close button as they can only be closed programmatically.
4920 cls               String           A custom CSS class to apply to the message box element
4921 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4922                                    displayed (defaults to 75)
4923 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4924                                    function will be btn (the name of the button that was clicked, if applicable,
4925                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4926                                    Progress and wait dialogs will ignore this option since they do not respond to
4927                                    user actions and can only be closed programmatically, so any required function
4928                                    should be called by the same code after it closes the dialog.
4929 icon              String           A CSS class that provides a background image to be used as an icon for
4930                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4931 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4932 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4933 modal             Boolean          False to allow user interaction with the page while the message box is
4934                                    displayed (defaults to true)
4935 msg               String           A string that will replace the existing message box body text (defaults
4936                                    to the XHTML-compliant non-breaking space character '&#160;')
4937 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4938 progress          Boolean          True to display a progress bar (defaults to false)
4939 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4940 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4941 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4942 title             String           The title text
4943 value             String           The string value to set into the active textbox element if displayed
4944 wait              Boolean          True to display a progress bar (defaults to false)
4945 width             Number           The width of the dialog in pixels
4946 </pre>
4947          *
4948          * Example usage:
4949          * <pre><code>
4950 Roo.Msg.show({
4951    title: 'Address',
4952    msg: 'Please enter your address:',
4953    width: 300,
4954    buttons: Roo.MessageBox.OKCANCEL,
4955    multiline: true,
4956    fn: saveAddress,
4957    animEl: 'addAddressBtn'
4958 });
4959 </code></pre>
4960          * @param {Object} config Configuration options
4961          * @return {Roo.MessageBox} This message box
4962          */
4963         show : function(options)
4964         {
4965             
4966             // this causes nightmares if you show one dialog after another
4967             // especially on callbacks..
4968              
4969             if(this.isVisible()){
4970                 
4971                 this.hide();
4972                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4973                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4974                 Roo.log("New Dialog Message:" +  options.msg )
4975                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4976                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4977                 
4978             }
4979             var d = this.getDialog();
4980             opt = options;
4981             d.setTitle(opt.title || "&#160;");
4982             d.closeEl.setDisplayed(opt.closable !== false);
4983             activeTextEl = textboxEl;
4984             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4985             if(opt.prompt){
4986                 if(opt.multiline){
4987                     textboxEl.hide();
4988                     textareaEl.show();
4989                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4990                         opt.multiline : this.defaultTextHeight);
4991                     activeTextEl = textareaEl;
4992                 }else{
4993                     textboxEl.show();
4994                     textareaEl.hide();
4995                 }
4996             }else{
4997                 textboxEl.hide();
4998                 textareaEl.hide();
4999             }
5000             progressEl.setDisplayed(opt.progress === true);
5001             if (opt.progress) {
5002                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5003             }
5004             this.updateProgress(0);
5005             activeTextEl.dom.value = opt.value || "";
5006             if(opt.prompt){
5007                 dlg.setDefaultButton(activeTextEl);
5008             }else{
5009                 var bs = opt.buttons;
5010                 var db = null;
5011                 if(bs && bs.ok){
5012                     db = buttons["ok"];
5013                 }else if(bs && bs.yes){
5014                     db = buttons["yes"];
5015                 }
5016                 dlg.setDefaultButton(db);
5017             }
5018             bwidth = updateButtons(opt.buttons);
5019             this.updateText(opt.msg);
5020             if(opt.cls){
5021                 d.el.addClass(opt.cls);
5022             }
5023             d.proxyDrag = opt.proxyDrag === true;
5024             d.modal = opt.modal !== false;
5025             d.mask = opt.modal !== false ? mask : false;
5026             if(!d.isVisible()){
5027                 // force it to the end of the z-index stack so it gets a cursor in FF
5028                 document.body.appendChild(dlg.el.dom);
5029                 d.animateTarget = null;
5030                 d.show(options.animEl);
5031             }
5032             return this;
5033         },
5034
5035         /**
5036          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5037          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5038          * and closing the message box when the process is complete.
5039          * @param {String} title The title bar text
5040          * @param {String} msg The message box body text
5041          * @return {Roo.MessageBox} This message box
5042          */
5043         progress : function(title, msg){
5044             this.show({
5045                 title : title,
5046                 msg : msg,
5047                 buttons: false,
5048                 progress:true,
5049                 closable:false,
5050                 minWidth: this.minProgressWidth,
5051                 modal : true
5052             });
5053             return this;
5054         },
5055
5056         /**
5057          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5058          * If a callback function is passed it will be called after the user clicks the button, and the
5059          * id of the button that was clicked will be passed as the only parameter to the callback
5060          * (could also be the top-right close button).
5061          * @param {String} title The title bar text
5062          * @param {String} msg The message box body text
5063          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5064          * @param {Object} scope (optional) The scope of the callback function
5065          * @return {Roo.MessageBox} This message box
5066          */
5067         alert : function(title, msg, fn, scope)
5068         {
5069             this.show({
5070                 title : title,
5071                 msg : msg,
5072                 buttons: this.OK,
5073                 fn: fn,
5074                 closable : false,
5075                 scope : scope,
5076                 modal : true
5077             });
5078             return this;
5079         },
5080
5081         /**
5082          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5083          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5084          * You are responsible for closing the message box when the process is complete.
5085          * @param {String} msg The message box body text
5086          * @param {String} title (optional) The title bar text
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         wait : function(msg, title){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: false,
5094                 closable:false,
5095                 progress:true,
5096                 modal:true,
5097                 width:300,
5098                 wait:true
5099             });
5100             waitTimer = Roo.TaskMgr.start({
5101                 run: function(i){
5102                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5103                 },
5104                 interval: 1000
5105             });
5106             return this;
5107         },
5108
5109         /**
5110          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5111          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5112          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5113          * @param {String} title The title bar text
5114          * @param {String} msg The message box body text
5115          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5116          * @param {Object} scope (optional) The scope of the callback function
5117          * @return {Roo.MessageBox} This message box
5118          */
5119         confirm : function(title, msg, fn, scope){
5120             this.show({
5121                 title : title,
5122                 msg : msg,
5123                 buttons: this.YESNO,
5124                 fn: fn,
5125                 scope : scope,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5133          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5134          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5135          * (could also be the top-right close button) and the text that was entered will be passed as the two
5136          * parameters to the callback.
5137          * @param {String} title The title bar text
5138          * @param {String} msg The message box body text
5139          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5140          * @param {Object} scope (optional) The scope of the callback function
5141          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5142          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5143          * @return {Roo.MessageBox} This message box
5144          */
5145         prompt : function(title, msg, fn, scope, multiline){
5146             this.show({
5147                 title : title,
5148                 msg : msg,
5149                 buttons: this.OKCANCEL,
5150                 fn: fn,
5151                 minWidth:250,
5152                 scope : scope,
5153                 prompt:true,
5154                 multiline: multiline,
5155                 modal : true
5156             });
5157             return this;
5158         },
5159
5160         /**
5161          * Button config that displays a single OK button
5162          * @type Object
5163          */
5164         OK : {ok:true},
5165         /**
5166          * Button config that displays Yes and No buttons
5167          * @type Object
5168          */
5169         YESNO : {yes:true, no:true},
5170         /**
5171          * Button config that displays OK and Cancel buttons
5172          * @type Object
5173          */
5174         OKCANCEL : {ok:true, cancel:true},
5175         /**
5176          * Button config that displays Yes, No and Cancel buttons
5177          * @type Object
5178          */
5179         YESNOCANCEL : {yes:true, no:true, cancel:true},
5180
5181         /**
5182          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5183          * @type Number
5184          */
5185         defaultTextHeight : 75,
5186         /**
5187          * The maximum width in pixels of the message box (defaults to 600)
5188          * @type Number
5189          */
5190         maxWidth : 600,
5191         /**
5192          * The minimum width in pixels of the message box (defaults to 100)
5193          * @type Number
5194          */
5195         minWidth : 100,
5196         /**
5197          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5198          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5199          * @type Number
5200          */
5201         minProgressWidth : 250,
5202         /**
5203          * An object containing the default button text strings that can be overriden for localized language support.
5204          * Supported properties are: ok, cancel, yes and no.
5205          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5206          * @type Object
5207          */
5208         buttonText : {
5209             ok : "OK",
5210             cancel : "Cancel",
5211             yes : "Yes",
5212             no : "No"
5213         }
5214     };
5215 }();
5216
5217 /**
5218  * Shorthand for {@link Roo.MessageBox}
5219  */
5220 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5221 Roo.Msg = Roo.Msg || Roo.MessageBox;
5222 /*
5223  * - LGPL
5224  *
5225  * navbar
5226  * 
5227  */
5228
5229 /**
5230  * @class Roo.bootstrap.Navbar
5231  * @extends Roo.bootstrap.Component
5232  * Bootstrap Navbar class
5233
5234  * @constructor
5235  * Create a new Navbar
5236  * @param {Object} config The config object
5237  */
5238
5239
5240 Roo.bootstrap.Navbar = function(config){
5241     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5242     this.addEvents({
5243         // raw events
5244         /**
5245          * @event beforetoggle
5246          * Fire before toggle the menu
5247          * @param {Roo.EventObject} e
5248          */
5249         "beforetoggle" : true
5250     });
5251 };
5252
5253 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5254     
5255     
5256    
5257     // private
5258     navItems : false,
5259     loadMask : false,
5260     
5261     
5262     getAutoCreate : function(){
5263         
5264         
5265         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5266         
5267     },
5268     
5269     initEvents :function ()
5270     {
5271         //Roo.log(this.el.select('.navbar-toggle',true));
5272         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5273         
5274         var mark = {
5275             tag: "div",
5276             cls:"x-dlg-mask"
5277         };
5278         
5279         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5280         
5281         var size = this.el.getSize();
5282         this.maskEl.setSize(size.width, size.height);
5283         this.maskEl.enableDisplayMode("block");
5284         this.maskEl.hide();
5285         
5286         if(this.loadMask){
5287             this.maskEl.show();
5288         }
5289     },
5290     
5291     
5292     getChildContainer : function()
5293     {
5294         if (this.el && this.el.select('.collapse').getCount()) {
5295             return this.el.select('.collapse',true).first();
5296         }
5297         
5298         return this.el;
5299     },
5300     
5301     mask : function()
5302     {
5303         this.maskEl.show();
5304     },
5305     
5306     unmask : function()
5307     {
5308         this.maskEl.hide();
5309     },
5310     onToggle : function()
5311     {
5312         
5313         if(this.fireEvent('beforetoggle', this) === false){
5314             return;
5315         }
5316         var ce = this.el.select('.navbar-collapse',true).first();
5317       
5318         if (!ce.hasClass('show')) {
5319            this.expand();
5320         } else {
5321             this.collapse();
5322         }
5323         
5324         
5325     
5326     },
5327     /**
5328      * Expand the navbar pulldown 
5329      */
5330     expand : function ()
5331     {
5332        
5333         var ce = this.el.select('.navbar-collapse',true).first();
5334         if (ce.hasClass('collapsing')) {
5335             return;
5336         }
5337         ce.dom.style.height = '';
5338                // show it...
5339         ce.addClass('in'); // old...
5340         ce.removeClass('collapse');
5341         ce.addClass('show');
5342         var h = ce.getHeight();
5343         Roo.log(h);
5344         ce.removeClass('show');
5345         // at this point we should be able to see it..
5346         ce.addClass('collapsing');
5347         
5348         ce.setHeight(0); // resize it ...
5349         ce.on('transitionend', function() {
5350             //Roo.log('done transition');
5351             ce.removeClass('collapsing');
5352             ce.addClass('show');
5353             ce.removeClass('collapse');
5354
5355             ce.dom.style.height = '';
5356         }, this, { single: true} );
5357         ce.setHeight(h);
5358         ce.dom.scrollTop = 0;
5359     },
5360     /**
5361      * Collapse the navbar pulldown 
5362      */
5363     collapse : function()
5364     {
5365          var ce = this.el.select('.navbar-collapse',true).first();
5366        
5367         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5368             // it's collapsed or collapsing..
5369             return;
5370         }
5371         ce.removeClass('in'); // old...
5372         ce.setHeight(ce.getHeight());
5373         ce.removeClass('show');
5374         ce.addClass('collapsing');
5375         
5376         ce.on('transitionend', function() {
5377             ce.dom.style.height = '';
5378             ce.removeClass('collapsing');
5379             ce.addClass('collapse');
5380         }, this, { single: true} );
5381         ce.setHeight(0);
5382     }
5383     
5384     
5385     
5386 });
5387
5388
5389
5390  
5391
5392  /*
5393  * - LGPL
5394  *
5395  * navbar
5396  * 
5397  */
5398
5399 /**
5400  * @class Roo.bootstrap.NavSimplebar
5401  * @extends Roo.bootstrap.Navbar
5402  * Bootstrap Sidebar class
5403  *
5404  * @cfg {Boolean} inverse is inverted color
5405  * 
5406  * @cfg {String} type (nav | pills | tabs)
5407  * @cfg {Boolean} arrangement stacked | justified
5408  * @cfg {String} align (left | right) alignment
5409  * 
5410  * @cfg {Boolean} main (true|false) main nav bar? default false
5411  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5412  * 
5413  * @cfg {String} tag (header|footer|nav|div) default is nav 
5414
5415  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5416  * 
5417  * 
5418  * @constructor
5419  * Create a new Sidebar
5420  * @param {Object} config The config object
5421  */
5422
5423
5424 Roo.bootstrap.NavSimplebar = function(config){
5425     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5426 };
5427
5428 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5429     
5430     inverse: false,
5431     
5432     type: false,
5433     arrangement: '',
5434     align : false,
5435     
5436     weight : 'light',
5437     
5438     main : false,
5439     
5440     
5441     tag : false,
5442     
5443     
5444     getAutoCreate : function(){
5445         
5446         
5447         var cfg = {
5448             tag : this.tag || 'div',
5449             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5450         };
5451         if (['light','white'].indexOf(this.weight) > -1) {
5452             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5453         }
5454         cfg.cls += ' bg-' + this.weight;
5455         
5456         if (this.inverse) {
5457             cfg.cls += ' navbar-inverse';
5458             
5459         }
5460         
5461         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5462         
5463         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5464             return cfg;
5465         }
5466         
5467         
5468     
5469         
5470         cfg.cn = [
5471             {
5472                 cls: 'nav nav-' + this.xtype,
5473                 tag : 'ul'
5474             }
5475         ];
5476         
5477          
5478         this.type = this.type || 'nav';
5479         if (['tabs','pills'].indexOf(this.type) != -1) {
5480             cfg.cn[0].cls += ' nav-' + this.type
5481         
5482         
5483         } else {
5484             if (this.type!=='nav') {
5485                 Roo.log('nav type must be nav/tabs/pills')
5486             }
5487             cfg.cn[0].cls += ' navbar-nav'
5488         }
5489         
5490         
5491         
5492         
5493         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5494             cfg.cn[0].cls += ' nav-' + this.arrangement;
5495         }
5496         
5497         
5498         if (this.align === 'right') {
5499             cfg.cn[0].cls += ' navbar-right';
5500         }
5501         
5502         
5503         
5504         
5505         return cfg;
5506     
5507         
5508     }
5509     
5510     
5511     
5512 });
5513
5514
5515
5516  
5517
5518  
5519        /*
5520  * - LGPL
5521  *
5522  * navbar
5523  * navbar-fixed-top
5524  * navbar-expand-md  fixed-top 
5525  */
5526
5527 /**
5528  * @class Roo.bootstrap.NavHeaderbar
5529  * @extends Roo.bootstrap.NavSimplebar
5530  * Bootstrap Sidebar class
5531  *
5532  * @cfg {String} brand what is brand
5533  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5534  * @cfg {String} brand_href href of the brand
5535  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5536  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5537  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5538  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5539  * 
5540  * @constructor
5541  * Create a new Sidebar
5542  * @param {Object} config The config object
5543  */
5544
5545
5546 Roo.bootstrap.NavHeaderbar = function(config){
5547     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5548       
5549 };
5550
5551 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5552     
5553     position: '',
5554     brand: '',
5555     brand_href: false,
5556     srButton : true,
5557     autohide : false,
5558     desktopCenter : false,
5559    
5560     
5561     getAutoCreate : function(){
5562         
5563         var   cfg = {
5564             tag: this.nav || 'nav',
5565             cls: 'navbar navbar-expand-md',
5566             role: 'navigation',
5567             cn: []
5568         };
5569         
5570         var cn = cfg.cn;
5571         if (this.desktopCenter) {
5572             cn.push({cls : 'container', cn : []});
5573             cn = cn[0].cn;
5574         }
5575         
5576         if(this.srButton){
5577             var btn = {
5578                 tag: 'button',
5579                 type: 'button',
5580                 cls: 'navbar-toggle navbar-toggler',
5581                 'data-toggle': 'collapse',
5582                 cn: [
5583                     {
5584                         tag: 'span',
5585                         cls: 'sr-only',
5586                         html: 'Toggle navigation'
5587                     },
5588                     {
5589                         tag: 'span',
5590                         cls: 'icon-bar navbar-toggler-icon'
5591                     },
5592                     {
5593                         tag: 'span',
5594                         cls: 'icon-bar'
5595                     },
5596                     {
5597                         tag: 'span',
5598                         cls: 'icon-bar'
5599                     }
5600                 ]
5601             };
5602             
5603             cn.push( Roo.bootstrap.version == 4 ? btn : {
5604                 tag: 'div',
5605                 cls: 'navbar-header',
5606                 cn: [
5607                     btn
5608                 ]
5609             });
5610         }
5611         
5612         cn.push({
5613             tag: 'div',
5614             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5615             cn : []
5616         });
5617         
5618         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5619         
5620         if (['light','white'].indexOf(this.weight) > -1) {
5621             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5622         }
5623         cfg.cls += ' bg-' + this.weight;
5624         
5625         
5626         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5627             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5628             
5629             // tag can override this..
5630             
5631             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5632         }
5633         
5634         if (this.brand !== '') {
5635             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5636             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5637                 tag: 'a',
5638                 href: this.brand_href ? this.brand_href : '#',
5639                 cls: 'navbar-brand',
5640                 cn: [
5641                 this.brand
5642                 ]
5643             });
5644         }
5645         
5646         if(this.main){
5647             cfg.cls += ' main-nav';
5648         }
5649         
5650         
5651         return cfg;
5652
5653         
5654     },
5655     getHeaderChildContainer : function()
5656     {
5657         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5658             return this.el.select('.navbar-header',true).first();
5659         }
5660         
5661         return this.getChildContainer();
5662     },
5663     
5664     getChildContainer : function()
5665     {
5666          
5667         return this.el.select('.roo-navbar-collapse',true).first();
5668          
5669         
5670     },
5671     
5672     initEvents : function()
5673     {
5674         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5675         
5676         if (this.autohide) {
5677             
5678             var prevScroll = 0;
5679             var ft = this.el;
5680             
5681             Roo.get(document).on('scroll',function(e) {
5682                 var ns = Roo.get(document).getScroll().top;
5683                 var os = prevScroll;
5684                 prevScroll = ns;
5685                 
5686                 if(ns > os){
5687                     ft.removeClass('slideDown');
5688                     ft.addClass('slideUp');
5689                     return;
5690                 }
5691                 ft.removeClass('slideUp');
5692                 ft.addClass('slideDown');
5693                  
5694               
5695           },this);
5696         }
5697     }    
5698     
5699 });
5700
5701
5702
5703  
5704
5705  /*
5706  * - LGPL
5707  *
5708  * navbar
5709  * 
5710  */
5711
5712 /**
5713  * @class Roo.bootstrap.NavSidebar
5714  * @extends Roo.bootstrap.Navbar
5715  * Bootstrap Sidebar class
5716  * 
5717  * @constructor
5718  * Create a new Sidebar
5719  * @param {Object} config The config object
5720  */
5721
5722
5723 Roo.bootstrap.NavSidebar = function(config){
5724     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5725 };
5726
5727 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5728     
5729     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         return  {
5735             tag: 'div',
5736             cls: 'sidebar sidebar-nav'
5737         };
5738     
5739         
5740     }
5741     
5742     
5743     
5744 });
5745
5746
5747
5748  
5749
5750  /*
5751  * - LGPL
5752  *
5753  * nav group
5754  * 
5755  */
5756
5757 /**
5758  * @class Roo.bootstrap.NavGroup
5759  * @extends Roo.bootstrap.Component
5760  * Bootstrap NavGroup class
5761  * @cfg {String} align (left|right)
5762  * @cfg {Boolean} inverse
5763  * @cfg {String} type (nav|pills|tab) default nav
5764  * @cfg {String} navId - reference Id for navbar.
5765  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5766  * 
5767  * @constructor
5768  * Create a new nav group
5769  * @param {Object} config The config object
5770  */
5771
5772 Roo.bootstrap.NavGroup = function(config){
5773     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5774     this.navItems = [];
5775    
5776     Roo.bootstrap.NavGroup.register(this);
5777      this.addEvents({
5778         /**
5779              * @event changed
5780              * Fires when the active item changes
5781              * @param {Roo.bootstrap.NavGroup} this
5782              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5783              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5784          */
5785         'changed': true
5786      });
5787     
5788 };
5789
5790 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5791     
5792     align: '',
5793     inverse: false,
5794     form: false,
5795     type: 'nav',
5796     navId : '',
5797     // private
5798     pilltype : true,
5799     
5800     navItems : false, 
5801     
5802     getAutoCreate : function()
5803     {
5804         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5805         
5806         cfg = {
5807             tag : 'ul',
5808             cls: 'nav' 
5809         };
5810         if (Roo.bootstrap.version == 4) {
5811             if (['tabs','pills'].indexOf(this.type) != -1) {
5812                 cfg.cls += ' nav-' + this.type; 
5813             } else {
5814                 // trying to remove so header bar can right align top?
5815                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5816                     // do not use on header bar... 
5817                     cfg.cls += ' navbar-nav';
5818                 }
5819             }
5820             
5821         } else {
5822             if (['tabs','pills'].indexOf(this.type) != -1) {
5823                 cfg.cls += ' nav-' + this.type
5824             } else {
5825                 if (this.type !== 'nav') {
5826                     Roo.log('nav type must be nav/tabs/pills')
5827                 }
5828                 cfg.cls += ' navbar-nav'
5829             }
5830         }
5831         
5832         if (this.parent() && this.parent().sidebar) {
5833             cfg = {
5834                 tag: 'ul',
5835                 cls: 'dashboard-menu sidebar-menu'
5836             };
5837             
5838             return cfg;
5839         }
5840         
5841         if (this.form === true) {
5842             cfg = {
5843                 tag: 'form',
5844                 cls: 'navbar-form form-inline'
5845             };
5846             //nav navbar-right ml-md-auto
5847             if (this.align === 'right') {
5848                 cfg.cls += ' navbar-right ml-md-auto';
5849             } else {
5850                 cfg.cls += ' navbar-left';
5851             }
5852         }
5853         
5854         if (this.align === 'right') {
5855             cfg.cls += ' navbar-right ml-md-auto';
5856         } else {
5857             cfg.cls += ' mr-auto';
5858         }
5859         
5860         if (this.inverse) {
5861             cfg.cls += ' navbar-inverse';
5862             
5863         }
5864         
5865         
5866         return cfg;
5867     },
5868     /**
5869     * sets the active Navigation item
5870     * @param {Roo.bootstrap.NavItem} the new current navitem
5871     */
5872     setActiveItem : function(item)
5873     {
5874         var prev = false;
5875         Roo.each(this.navItems, function(v){
5876             if (v == item) {
5877                 return ;
5878             }
5879             if (v.isActive()) {
5880                 v.setActive(false, true);
5881                 prev = v;
5882                 
5883             }
5884             
5885         });
5886
5887         item.setActive(true, true);
5888         this.fireEvent('changed', this, item, prev);
5889         
5890         
5891     },
5892     /**
5893     * gets the active Navigation item
5894     * @return {Roo.bootstrap.NavItem} the current navitem
5895     */
5896     getActive : function()
5897     {
5898         
5899         var prev = false;
5900         Roo.each(this.navItems, function(v){
5901             
5902             if (v.isActive()) {
5903                 prev = v;
5904                 
5905             }
5906             
5907         });
5908         return prev;
5909     },
5910     
5911     indexOfNav : function()
5912     {
5913         
5914         var prev = false;
5915         Roo.each(this.navItems, function(v,i){
5916             
5917             if (v.isActive()) {
5918                 prev = i;
5919                 
5920             }
5921             
5922         });
5923         return prev;
5924     },
5925     /**
5926     * adds a Navigation item
5927     * @param {Roo.bootstrap.NavItem} the navitem to add
5928     */
5929     addItem : function(cfg)
5930     {
5931         if (this.form && Roo.bootstrap.version == 4) {
5932             cfg.tag = 'div';
5933         }
5934         var cn = new Roo.bootstrap.NavItem(cfg);
5935         this.register(cn);
5936         cn.parentId = this.id;
5937         cn.onRender(this.el, null);
5938         return cn;
5939     },
5940     /**
5941     * register a Navigation item
5942     * @param {Roo.bootstrap.NavItem} the navitem to add
5943     */
5944     register : function(item)
5945     {
5946         this.navItems.push( item);
5947         item.navId = this.navId;
5948     
5949     },
5950     
5951     /**
5952     * clear all the Navigation item
5953     */
5954    
5955     clearAll : function()
5956     {
5957         this.navItems = [];
5958         this.el.dom.innerHTML = '';
5959     },
5960     
5961     getNavItem: function(tabId)
5962     {
5963         var ret = false;
5964         Roo.each(this.navItems, function(e) {
5965             if (e.tabId == tabId) {
5966                ret =  e;
5967                return false;
5968             }
5969             return true;
5970             
5971         });
5972         return ret;
5973     },
5974     
5975     setActiveNext : function()
5976     {
5977         var i = this.indexOfNav(this.getActive());
5978         if (i > this.navItems.length) {
5979             return;
5980         }
5981         this.setActiveItem(this.navItems[i+1]);
5982     },
5983     setActivePrev : function()
5984     {
5985         var i = this.indexOfNav(this.getActive());
5986         if (i  < 1) {
5987             return;
5988         }
5989         this.setActiveItem(this.navItems[i-1]);
5990     },
5991     clearWasActive : function(except) {
5992         Roo.each(this.navItems, function(e) {
5993             if (e.tabId != except.tabId && e.was_active) {
5994                e.was_active = false;
5995                return false;
5996             }
5997             return true;
5998             
5999         });
6000     },
6001     getWasActive : function ()
6002     {
6003         var r = false;
6004         Roo.each(this.navItems, function(e) {
6005             if (e.was_active) {
6006                r = e;
6007                return false;
6008             }
6009             return true;
6010             
6011         });
6012         return r;
6013     }
6014     
6015     
6016 });
6017
6018  
6019 Roo.apply(Roo.bootstrap.NavGroup, {
6020     
6021     groups: {},
6022      /**
6023     * register a Navigation Group
6024     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6025     */
6026     register : function(navgrp)
6027     {
6028         this.groups[navgrp.navId] = navgrp;
6029         
6030     },
6031     /**
6032     * fetch a Navigation Group based on the navigation ID
6033     * @param {string} the navgroup to add
6034     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6035     */
6036     get: function(navId) {
6037         if (typeof(this.groups[navId]) == 'undefined') {
6038             return false;
6039             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6040         }
6041         return this.groups[navId] ;
6042     }
6043     
6044     
6045     
6046 });
6047
6048  /*
6049  * - LGPL
6050  *
6051  * row
6052  * 
6053  */
6054
6055 /**
6056  * @class Roo.bootstrap.NavItem
6057  * @extends Roo.bootstrap.Component
6058  * Bootstrap Navbar.NavItem class
6059  * @cfg {String} href  link to
6060  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6061  * @cfg {Boolean} button_outline show and outlined button
6062  * @cfg {String} html content of button
6063  * @cfg {String} badge text inside badge
6064  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6065  * @cfg {String} glyphicon DEPRICATED - use fa
6066  * @cfg {String} icon DEPRICATED - use fa
6067  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6068  * @cfg {Boolean} active Is item active
6069  * @cfg {Boolean} disabled Is item disabled
6070  * @cfg {String} linkcls  Link Class
6071  * @cfg {Boolean} preventDefault (true | false) default false
6072  * @cfg {String} tabId the tab that this item activates.
6073  * @cfg {String} tagtype (a|span) render as a href or span?
6074  * @cfg {Boolean} animateRef (true|false) link to element default false  
6075   
6076  * @constructor
6077  * Create a new Navbar Item
6078  * @param {Object} config The config object
6079  */
6080 Roo.bootstrap.NavItem = function(config){
6081     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6082     this.addEvents({
6083         // raw events
6084         /**
6085          * @event click
6086          * The raw click event for the entire grid.
6087          * @param {Roo.EventObject} e
6088          */
6089         "click" : true,
6090          /**
6091             * @event changed
6092             * Fires when the active item active state changes
6093             * @param {Roo.bootstrap.NavItem} this
6094             * @param {boolean} state the new state
6095              
6096          */
6097         'changed': true,
6098         /**
6099             * @event scrollto
6100             * Fires when scroll to element
6101             * @param {Roo.bootstrap.NavItem} this
6102             * @param {Object} options
6103             * @param {Roo.EventObject} e
6104              
6105          */
6106         'scrollto': true
6107     });
6108    
6109 };
6110
6111 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6112     
6113     href: false,
6114     html: '',
6115     badge: '',
6116     icon: false,
6117     fa : false,
6118     glyphicon: false,
6119     active: false,
6120     preventDefault : false,
6121     tabId : false,
6122     tagtype : 'a',
6123     tag: 'li',
6124     disabled : false,
6125     animateRef : false,
6126     was_active : false,
6127     button_weight : '',
6128     button_outline : false,
6129     linkcls : '',
6130     navLink: false,
6131     
6132     getAutoCreate : function(){
6133          
6134         var cfg = {
6135             tag: this.tag,
6136             cls: 'nav-item'
6137         };
6138         
6139         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6140         
6141         if (this.active) {
6142             cfg.cls +=  ' active' ;
6143         }
6144         if (this.disabled) {
6145             cfg.cls += ' disabled';
6146         }
6147         
6148         // BS4 only?
6149         if (this.button_weight.length) {
6150             cfg.tag = this.href ? 'a' : 'button';
6151             cfg.html = this.html || '';
6152             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6153             if (this.href) {
6154                 cfg.href = this.href;
6155             }
6156             if (this.fa) {
6157                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6158             }
6159             
6160             // menu .. should add dropdown-menu class - so no need for carat..
6161             
6162             if (this.badge !== '') {
6163                  
6164                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6165             }
6166             return cfg;
6167         }
6168         
6169         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6170             cfg.cn = [
6171                 {
6172                     tag: this.tagtype,
6173                     href : this.href || "#",
6174                     html: this.html || ''
6175                 }
6176             ];
6177             if (this.tagtype == 'a') {
6178                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6179         
6180             }
6181             if (this.icon) {
6182                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6183             }
6184             if (this.fa) {
6185                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6186             }
6187             if(this.glyphicon) {
6188                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6189             }
6190             
6191             if (this.menu) {
6192                 
6193                 cfg.cn[0].html += " <span class='caret'></span>";
6194              
6195             }
6196             
6197             if (this.badge !== '') {
6198                  
6199                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6200             }
6201         }
6202         
6203         
6204         
6205         return cfg;
6206     },
6207     onRender : function(ct, position)
6208     {
6209        // Roo.log("Call onRender: " + this.xtype);
6210         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6211             this.tag = 'div';
6212         }
6213         
6214         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6215         this.navLink = this.el.select('.nav-link',true).first();
6216         return ret;
6217     },
6218       
6219     
6220     initEvents: function() 
6221     {
6222         if (typeof (this.menu) != 'undefined') {
6223             this.menu.parentType = this.xtype;
6224             this.menu.triggerEl = this.el;
6225             this.menu = this.addxtype(Roo.apply({}, this.menu));
6226         }
6227         
6228         this.el.on('click', this.onClick, this);
6229         
6230         //if(this.tagtype == 'span'){
6231         //    this.el.select('span',true).on('click', this.onClick, this);
6232         //}
6233        
6234         // at this point parent should be available..
6235         this.parent().register(this);
6236     },
6237     
6238     onClick : function(e)
6239     {
6240         if (e.getTarget('.dropdown-menu-item')) {
6241             // did you click on a menu itemm.... - then don't trigger onclick..
6242             return;
6243         }
6244         
6245         if(
6246                 this.preventDefault || 
6247                 this.href == '#' 
6248         ){
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251         }
6252         
6253         if (this.disabled) {
6254             return;
6255         }
6256         
6257         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6258         if (tg && tg.transition) {
6259             Roo.log("waiting for the transitionend");
6260             return;
6261         }
6262         
6263         
6264         
6265         //Roo.log("fire event clicked");
6266         if(this.fireEvent('click', this, e) === false){
6267             return;
6268         };
6269         
6270         if(this.tagtype == 'span'){
6271             return;
6272         }
6273         
6274         //Roo.log(this.href);
6275         var ael = this.el.select('a',true).first();
6276         //Roo.log(ael);
6277         
6278         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6279             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6280             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6281                 return; // ignore... - it's a 'hash' to another page.
6282             }
6283             Roo.log("NavItem - prevent Default?");
6284             e.preventDefault();
6285             this.scrollToElement(e);
6286         }
6287         
6288         
6289         var p =  this.parent();
6290    
6291         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6292             if (typeof(p.setActiveItem) !== 'undefined') {
6293                 p.setActiveItem(this);
6294             }
6295         }
6296         
6297         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6298         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6299             // remove the collapsed menu expand...
6300             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6301         }
6302     },
6303     
6304     isActive: function () {
6305         return this.active
6306     },
6307     setActive : function(state, fire, is_was_active)
6308     {
6309         if (this.active && !state && this.navId) {
6310             this.was_active = true;
6311             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6312             if (nv) {
6313                 nv.clearWasActive(this);
6314             }
6315             
6316         }
6317         this.active = state;
6318         
6319         if (!state ) {
6320             this.el.removeClass('active');
6321             this.navLink ? this.navLink.removeClass('active') : false;
6322         } else if (!this.el.hasClass('active')) {
6323             
6324             this.el.addClass('active');
6325             if (Roo.bootstrap.version == 4 && this.navLink ) {
6326                 this.navLink.addClass('active');
6327             }
6328             
6329         }
6330         if (fire) {
6331             this.fireEvent('changed', this, state);
6332         }
6333         
6334         // show a panel if it's registered and related..
6335         
6336         if (!this.navId || !this.tabId || !state || is_was_active) {
6337             return;
6338         }
6339         
6340         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6341         if (!tg) {
6342             return;
6343         }
6344         var pan = tg.getPanelByName(this.tabId);
6345         if (!pan) {
6346             return;
6347         }
6348         // if we can not flip to new panel - go back to old nav highlight..
6349         if (false == tg.showPanel(pan)) {
6350             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6351             if (nv) {
6352                 var onav = nv.getWasActive();
6353                 if (onav) {
6354                     onav.setActive(true, false, true);
6355                 }
6356             }
6357             
6358         }
6359         
6360         
6361         
6362     },
6363      // this should not be here...
6364     setDisabled : function(state)
6365     {
6366         this.disabled = state;
6367         if (!state ) {
6368             this.el.removeClass('disabled');
6369         } else if (!this.el.hasClass('disabled')) {
6370             this.el.addClass('disabled');
6371         }
6372         
6373     },
6374     
6375     /**
6376      * Fetch the element to display the tooltip on.
6377      * @return {Roo.Element} defaults to this.el
6378      */
6379     tooltipEl : function()
6380     {
6381         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6382     },
6383     
6384     scrollToElement : function(e)
6385     {
6386         var c = document.body;
6387         
6388         /*
6389          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6390          */
6391         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6392             c = document.documentElement;
6393         }
6394         
6395         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6396         
6397         if(!target){
6398             return;
6399         }
6400
6401         var o = target.calcOffsetsTo(c);
6402         
6403         var options = {
6404             target : target,
6405             value : o[1]
6406         };
6407         
6408         this.fireEvent('scrollto', this, options, e);
6409         
6410         Roo.get(c).scrollTo('top', options.value, true);
6411         
6412         return;
6413     }
6414 });
6415  
6416
6417  /*
6418  * - LGPL
6419  *
6420  * sidebar item
6421  *
6422  *  li
6423  *    <span> icon </span>
6424  *    <span> text </span>
6425  *    <span>badge </span>
6426  */
6427
6428 /**
6429  * @class Roo.bootstrap.NavSidebarItem
6430  * @extends Roo.bootstrap.NavItem
6431  * Bootstrap Navbar.NavSidebarItem class
6432  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6433  * {Boolean} open is the menu open
6434  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6435  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6436  * {String} buttonSize (sm|md|lg)the extra classes for the button
6437  * {Boolean} showArrow show arrow next to the text (default true)
6438  * @constructor
6439  * Create a new Navbar Button
6440  * @param {Object} config The config object
6441  */
6442 Roo.bootstrap.NavSidebarItem = function(config){
6443     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6444     this.addEvents({
6445         // raw events
6446         /**
6447          * @event click
6448          * The raw click event for the entire grid.
6449          * @param {Roo.EventObject} e
6450          */
6451         "click" : true,
6452          /**
6453             * @event changed
6454             * Fires when the active item active state changes
6455             * @param {Roo.bootstrap.NavSidebarItem} this
6456             * @param {boolean} state the new state
6457              
6458          */
6459         'changed': true
6460     });
6461    
6462 };
6463
6464 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6465     
6466     badgeWeight : 'default',
6467     
6468     open: false,
6469     
6470     buttonView : false,
6471     
6472     buttonWeight : 'default',
6473     
6474     buttonSize : 'md',
6475     
6476     showArrow : true,
6477     
6478     getAutoCreate : function(){
6479         
6480         
6481         var a = {
6482                 tag: 'a',
6483                 href : this.href || '#',
6484                 cls: '',
6485                 html : '',
6486                 cn : []
6487         };
6488         
6489         if(this.buttonView){
6490             a = {
6491                 tag: 'button',
6492                 href : this.href || '#',
6493                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6494                 html : this.html,
6495                 cn : []
6496             };
6497         }
6498         
6499         var cfg = {
6500             tag: 'li',
6501             cls: '',
6502             cn: [ a ]
6503         };
6504         
6505         if (this.active) {
6506             cfg.cls += ' active';
6507         }
6508         
6509         if (this.disabled) {
6510             cfg.cls += ' disabled';
6511         }
6512         if (this.open) {
6513             cfg.cls += ' open x-open';
6514         }
6515         // left icon..
6516         if (this.glyphicon || this.icon) {
6517             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6518             a.cn.push({ tag : 'i', cls : c }) ;
6519         }
6520         
6521         if(!this.buttonView){
6522             var span = {
6523                 tag: 'span',
6524                 html : this.html || ''
6525             };
6526
6527             a.cn.push(span);
6528             
6529         }
6530         
6531         if (this.badge !== '') {
6532             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6533         }
6534         
6535         if (this.menu) {
6536             
6537             if(this.showArrow){
6538                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6539             }
6540             
6541             a.cls += ' dropdown-toggle treeview' ;
6542         }
6543         
6544         return cfg;
6545     },
6546     
6547     initEvents : function()
6548     { 
6549         if (typeof (this.menu) != 'undefined') {
6550             this.menu.parentType = this.xtype;
6551             this.menu.triggerEl = this.el;
6552             this.menu = this.addxtype(Roo.apply({}, this.menu));
6553         }
6554         
6555         this.el.on('click', this.onClick, this);
6556         
6557         if(this.badge !== ''){
6558             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6559         }
6560         
6561     },
6562     
6563     onClick : function(e)
6564     {
6565         if(this.disabled){
6566             e.preventDefault();
6567             return;
6568         }
6569         
6570         if(this.preventDefault){
6571             e.preventDefault();
6572         }
6573         
6574         this.fireEvent('click', this, e);
6575     },
6576     
6577     disable : function()
6578     {
6579         this.setDisabled(true);
6580     },
6581     
6582     enable : function()
6583     {
6584         this.setDisabled(false);
6585     },
6586     
6587     setDisabled : function(state)
6588     {
6589         if(this.disabled == state){
6590             return;
6591         }
6592         
6593         this.disabled = state;
6594         
6595         if (state) {
6596             this.el.addClass('disabled');
6597             return;
6598         }
6599         
6600         this.el.removeClass('disabled');
6601         
6602         return;
6603     },
6604     
6605     setActive : function(state)
6606     {
6607         if(this.active == state){
6608             return;
6609         }
6610         
6611         this.active = state;
6612         
6613         if (state) {
6614             this.el.addClass('active');
6615             return;
6616         }
6617         
6618         this.el.removeClass('active');
6619         
6620         return;
6621     },
6622     
6623     isActive: function () 
6624     {
6625         return this.active;
6626     },
6627     
6628     setBadge : function(str)
6629     {
6630         if(!this.badgeEl){
6631             return;
6632         }
6633         
6634         this.badgeEl.dom.innerHTML = str;
6635     }
6636     
6637    
6638      
6639  
6640 });
6641  
6642
6643  /*
6644  * - LGPL
6645  *
6646  *  Breadcrumb Nav
6647  * 
6648  */
6649 Roo.namespace('Roo.bootstrap.breadcrumb');
6650
6651
6652 /**
6653  * @class Roo.bootstrap.breadcrumb.Nav
6654  * @extends Roo.bootstrap.Component
6655  * Bootstrap Breadcrumb Nav Class
6656  *  
6657  * @children Roo.bootstrap.breadcrumb.Item
6658  * 
6659  * @constructor
6660  * Create a new breadcrumb.Nav
6661  * @param {Object} config The config object
6662  */
6663
6664
6665 Roo.bootstrap.breadcrumb.Nav = function(config){
6666     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6667     
6668     
6669 };
6670
6671 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6672     
6673     getAutoCreate : function()
6674     {
6675
6676         var cfg = {
6677             tag: 'nav',
6678             cn : [
6679                 {
6680                     tag : 'ol',
6681                     cls : 'breadcrumb'
6682                 }
6683             ]
6684             
6685         };
6686           
6687         return cfg;
6688     },
6689     
6690     initEvents: function()
6691     {
6692         this.olEl = this.el.select('ol',true).first();    
6693     },
6694     getChildContainer : function()
6695     {
6696         return this.olEl;  
6697     }
6698     
6699 });
6700
6701  /*
6702  * - LGPL
6703  *
6704  *  Breadcrumb Item
6705  * 
6706  */
6707
6708
6709 /**
6710  * @class Roo.bootstrap.breadcrumb.Nav
6711  * @extends Roo.bootstrap.Component
6712  * Bootstrap Breadcrumb Nav Class
6713  *  
6714  * @children Roo.bootstrap.breadcrumb.Component
6715  * @cfg {String} html the content of the link.
6716  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6717  * @cfg {Boolean} active is it active
6718
6719  * 
6720  * @constructor
6721  * Create a new breadcrumb.Nav
6722  * @param {Object} config The config object
6723  */
6724
6725 Roo.bootstrap.breadcrumb.Item = function(config){
6726     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6727     this.addEvents({
6728         // img events
6729         /**
6730          * @event click
6731          * The img click event for the img.
6732          * @param {Roo.EventObject} e
6733          */
6734         "click" : true
6735     });
6736     
6737 };
6738
6739 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6740     
6741     href: false,
6742     html : '',
6743     
6744     getAutoCreate : function()
6745     {
6746
6747         var cfg = {
6748             tag: 'li',
6749             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6750         };
6751         if (this.href !== false) {
6752             cfg.cn = [{
6753                 tag : 'a',
6754                 href : this.href,
6755                 html : this.html
6756             }];
6757         } else {
6758             cfg.html = this.html;
6759         }
6760         
6761         return cfg;
6762     },
6763     
6764     initEvents: function()
6765     {
6766         if (this.href) {
6767             this.el.select('a', true).first().on('click',this.onClick, this)
6768         }
6769         
6770     },
6771     onClick : function(e)
6772     {
6773         e.preventDefault();
6774         this.fireEvent('click',this,  e);
6775     }
6776     
6777 });
6778
6779  /*
6780  * - LGPL
6781  *
6782  * row
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Row
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Row class (contains columns...)
6790  * 
6791  * @constructor
6792  * Create a new Row
6793  * @param {Object} config The config object
6794  */
6795
6796 Roo.bootstrap.Row = function(config){
6797     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6798 };
6799
6800 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6801     
6802     getAutoCreate : function(){
6803        return {
6804             cls: 'row clearfix'
6805        };
6806     }
6807     
6808     
6809 });
6810
6811  
6812
6813  /*
6814  * - LGPL
6815  *
6816  * pagination
6817  * 
6818  */
6819
6820 /**
6821  * @class Roo.bootstrap.Pagination
6822  * @extends Roo.bootstrap.Component
6823  * Bootstrap Pagination class
6824  * @cfg {String} size xs | sm | md | lg
6825  * @cfg {Boolean} inverse false | true
6826  * 
6827  * @constructor
6828  * Create a new Pagination
6829  * @param {Object} config The config object
6830  */
6831
6832 Roo.bootstrap.Pagination = function(config){
6833     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6834 };
6835
6836 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6837     
6838     cls: false,
6839     size: false,
6840     inverse: false,
6841     
6842     getAutoCreate : function(){
6843         var cfg = {
6844             tag: 'ul',
6845                 cls: 'pagination'
6846         };
6847         if (this.inverse) {
6848             cfg.cls += ' inverse';
6849         }
6850         if (this.html) {
6851             cfg.html=this.html;
6852         }
6853         if (this.cls) {
6854             cfg.cls += " " + this.cls;
6855         }
6856         return cfg;
6857     }
6858    
6859 });
6860
6861  
6862
6863  /*
6864  * - LGPL
6865  *
6866  * Pagination item
6867  * 
6868  */
6869
6870
6871 /**
6872  * @class Roo.bootstrap.PaginationItem
6873  * @extends Roo.bootstrap.Component
6874  * Bootstrap PaginationItem class
6875  * @cfg {String} html text
6876  * @cfg {String} href the link
6877  * @cfg {Boolean} preventDefault (true | false) default true
6878  * @cfg {Boolean} active (true | false) default false
6879  * @cfg {Boolean} disabled default false
6880  * 
6881  * 
6882  * @constructor
6883  * Create a new PaginationItem
6884  * @param {Object} config The config object
6885  */
6886
6887
6888 Roo.bootstrap.PaginationItem = function(config){
6889     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6890     this.addEvents({
6891         // raw events
6892         /**
6893          * @event click
6894          * The raw click event for the entire grid.
6895          * @param {Roo.EventObject} e
6896          */
6897         "click" : true
6898     });
6899 };
6900
6901 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6902     
6903     href : false,
6904     html : false,
6905     preventDefault: true,
6906     active : false,
6907     cls : false,
6908     disabled: false,
6909     
6910     getAutoCreate : function(){
6911         var cfg= {
6912             tag: 'li',
6913             cn: [
6914                 {
6915                     tag : 'a',
6916                     href : this.href ? this.href : '#',
6917                     html : this.html ? this.html : ''
6918                 }
6919             ]
6920         };
6921         
6922         if(this.cls){
6923             cfg.cls = this.cls;
6924         }
6925         
6926         if(this.disabled){
6927             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6928         }
6929         
6930         if(this.active){
6931             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6932         }
6933         
6934         return cfg;
6935     },
6936     
6937     initEvents: function() {
6938         
6939         this.el.on('click', this.onClick, this);
6940         
6941     },
6942     onClick : function(e)
6943     {
6944         Roo.log('PaginationItem on click ');
6945         if(this.preventDefault){
6946             e.preventDefault();
6947         }
6948         
6949         if(this.disabled){
6950             return;
6951         }
6952         
6953         this.fireEvent('click', this, e);
6954     }
6955    
6956 });
6957
6958  
6959
6960  /*
6961  * - LGPL
6962  *
6963  * slider
6964  * 
6965  */
6966
6967
6968 /**
6969  * @class Roo.bootstrap.Slider
6970  * @extends Roo.bootstrap.Component
6971  * Bootstrap Slider class
6972  *    
6973  * @constructor
6974  * Create a new Slider
6975  * @param {Object} config The config object
6976  */
6977
6978 Roo.bootstrap.Slider = function(config){
6979     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6980 };
6981
6982 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6983     
6984     getAutoCreate : function(){
6985         
6986         var cfg = {
6987             tag: 'div',
6988             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6989             cn: [
6990                 {
6991                     tag: 'a',
6992                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6993                 }
6994             ]
6995         };
6996         
6997         return cfg;
6998     }
6999    
7000 });
7001
7002  /*
7003  * Based on:
7004  * Ext JS Library 1.1.1
7005  * Copyright(c) 2006-2007, Ext JS, LLC.
7006  *
7007  * Originally Released Under LGPL - original licence link has changed is not relivant.
7008  *
7009  * Fork - LGPL
7010  * <script type="text/javascript">
7011  */
7012  
7013
7014 /**
7015  * @class Roo.grid.ColumnModel
7016  * @extends Roo.util.Observable
7017  * This is the default implementation of a ColumnModel used by the Grid. It defines
7018  * the columns in the grid.
7019  * <br>Usage:<br>
7020  <pre><code>
7021  var colModel = new Roo.grid.ColumnModel([
7022         {header: "Ticker", width: 60, sortable: true, locked: true},
7023         {header: "Company Name", width: 150, sortable: true},
7024         {header: "Market Cap.", width: 100, sortable: true},
7025         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7026         {header: "Employees", width: 100, sortable: true, resizable: false}
7027  ]);
7028  </code></pre>
7029  * <p>
7030  
7031  * The config options listed for this class are options which may appear in each
7032  * individual column definition.
7033  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7034  * @constructor
7035  * @param {Object} config An Array of column config objects. See this class's
7036  * config objects for details.
7037 */
7038 Roo.grid.ColumnModel = function(config){
7039         /**
7040      * The config passed into the constructor
7041      */
7042     this.config = config;
7043     this.lookup = {};
7044
7045     // if no id, create one
7046     // if the column does not have a dataIndex mapping,
7047     // map it to the order it is in the config
7048     for(var i = 0, len = config.length; i < len; i++){
7049         var c = config[i];
7050         if(typeof c.dataIndex == "undefined"){
7051             c.dataIndex = i;
7052         }
7053         if(typeof c.renderer == "string"){
7054             c.renderer = Roo.util.Format[c.renderer];
7055         }
7056         if(typeof c.id == "undefined"){
7057             c.id = Roo.id();
7058         }
7059         if(c.editor && c.editor.xtype){
7060             c.editor  = Roo.factory(c.editor, Roo.grid);
7061         }
7062         if(c.editor && c.editor.isFormField){
7063             c.editor = new Roo.grid.GridEditor(c.editor);
7064         }
7065         this.lookup[c.id] = c;
7066     }
7067
7068     /**
7069      * The width of columns which have no width specified (defaults to 100)
7070      * @type Number
7071      */
7072     this.defaultWidth = 100;
7073
7074     /**
7075      * Default sortable of columns which have no sortable specified (defaults to false)
7076      * @type Boolean
7077      */
7078     this.defaultSortable = false;
7079
7080     this.addEvents({
7081         /**
7082              * @event widthchange
7083              * Fires when the width of a column changes.
7084              * @param {ColumnModel} this
7085              * @param {Number} columnIndex The column index
7086              * @param {Number} newWidth The new width
7087              */
7088             "widthchange": true,
7089         /**
7090              * @event headerchange
7091              * Fires when the text of a header changes.
7092              * @param {ColumnModel} this
7093              * @param {Number} columnIndex The column index
7094              * @param {Number} newText The new header text
7095              */
7096             "headerchange": true,
7097         /**
7098              * @event hiddenchange
7099              * Fires when a column is hidden or "unhidden".
7100              * @param {ColumnModel} this
7101              * @param {Number} columnIndex The column index
7102              * @param {Boolean} hidden true if hidden, false otherwise
7103              */
7104             "hiddenchange": true,
7105             /**
7106          * @event columnmoved
7107          * Fires when a column is moved.
7108          * @param {ColumnModel} this
7109          * @param {Number} oldIndex
7110          * @param {Number} newIndex
7111          */
7112         "columnmoved" : true,
7113         /**
7114          * @event columlockchange
7115          * Fires when a column's locked state is changed
7116          * @param {ColumnModel} this
7117          * @param {Number} colIndex
7118          * @param {Boolean} locked true if locked
7119          */
7120         "columnlockchange" : true
7121     });
7122     Roo.grid.ColumnModel.superclass.constructor.call(this);
7123 };
7124 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7125     /**
7126      * @cfg {String} header The header text to display in the Grid view.
7127      */
7128     /**
7129      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7130      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7131      * specified, the column's index is used as an index into the Record's data Array.
7132      */
7133     /**
7134      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7135      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7136      */
7137     /**
7138      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7139      * Defaults to the value of the {@link #defaultSortable} property.
7140      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7141      */
7142     /**
7143      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7144      */
7145     /**
7146      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7147      */
7148     /**
7149      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7150      */
7151     /**
7152      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7153      */
7154     /**
7155      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7156      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7157      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7158      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7159      */
7160        /**
7161      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7162      */
7163     /**
7164      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7165      */
7166     /**
7167      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7168      */
7169     /**
7170      * @cfg {String} cursor (Optional)
7171      */
7172     /**
7173      * @cfg {String} tooltip (Optional)
7174      */
7175     /**
7176      * @cfg {Number} xs (Optional)
7177      */
7178     /**
7179      * @cfg {Number} sm (Optional)
7180      */
7181     /**
7182      * @cfg {Number} md (Optional)
7183      */
7184     /**
7185      * @cfg {Number} lg (Optional)
7186      */
7187     /**
7188      * Returns the id of the column at the specified index.
7189      * @param {Number} index The column index
7190      * @return {String} the id
7191      */
7192     getColumnId : function(index){
7193         return this.config[index].id;
7194     },
7195
7196     /**
7197      * Returns the column for a specified id.
7198      * @param {String} id The column id
7199      * @return {Object} the column
7200      */
7201     getColumnById : function(id){
7202         return this.lookup[id];
7203     },
7204
7205     
7206     /**
7207      * Returns the column for a specified dataIndex.
7208      * @param {String} dataIndex The column dataIndex
7209      * @return {Object|Boolean} the column or false if not found
7210      */
7211     getColumnByDataIndex: function(dataIndex){
7212         var index = this.findColumnIndex(dataIndex);
7213         return index > -1 ? this.config[index] : false;
7214     },
7215     
7216     /**
7217      * Returns the index for a specified column id.
7218      * @param {String} id The column id
7219      * @return {Number} the index, or -1 if not found
7220      */
7221     getIndexById : function(id){
7222         for(var i = 0, len = this.config.length; i < len; i++){
7223             if(this.config[i].id == id){
7224                 return i;
7225             }
7226         }
7227         return -1;
7228     },
7229     
7230     /**
7231      * Returns the index for a specified column dataIndex.
7232      * @param {String} dataIndex The column dataIndex
7233      * @return {Number} the index, or -1 if not found
7234      */
7235     
7236     findColumnIndex : function(dataIndex){
7237         for(var i = 0, len = this.config.length; i < len; i++){
7238             if(this.config[i].dataIndex == dataIndex){
7239                 return i;
7240             }
7241         }
7242         return -1;
7243     },
7244     
7245     
7246     moveColumn : function(oldIndex, newIndex){
7247         var c = this.config[oldIndex];
7248         this.config.splice(oldIndex, 1);
7249         this.config.splice(newIndex, 0, c);
7250         this.dataMap = null;
7251         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7252     },
7253
7254     isLocked : function(colIndex){
7255         return this.config[colIndex].locked === true;
7256     },
7257
7258     setLocked : function(colIndex, value, suppressEvent){
7259         if(this.isLocked(colIndex) == value){
7260             return;
7261         }
7262         this.config[colIndex].locked = value;
7263         if(!suppressEvent){
7264             this.fireEvent("columnlockchange", this, colIndex, value);
7265         }
7266     },
7267
7268     getTotalLockedWidth : function(){
7269         var totalWidth = 0;
7270         for(var i = 0; i < this.config.length; i++){
7271             if(this.isLocked(i) && !this.isHidden(i)){
7272                 this.totalWidth += this.getColumnWidth(i);
7273             }
7274         }
7275         return totalWidth;
7276     },
7277
7278     getLockedCount : function(){
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             if(!this.isLocked(i)){
7281                 return i;
7282             }
7283         }
7284         
7285         return this.config.length;
7286     },
7287
7288     /**
7289      * Returns the number of columns.
7290      * @return {Number}
7291      */
7292     getColumnCount : function(visibleOnly){
7293         if(visibleOnly === true){
7294             var c = 0;
7295             for(var i = 0, len = this.config.length; i < len; i++){
7296                 if(!this.isHidden(i)){
7297                     c++;
7298                 }
7299             }
7300             return c;
7301         }
7302         return this.config.length;
7303     },
7304
7305     /**
7306      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7307      * @param {Function} fn
7308      * @param {Object} scope (optional)
7309      * @return {Array} result
7310      */
7311     getColumnsBy : function(fn, scope){
7312         var r = [];
7313         for(var i = 0, len = this.config.length; i < len; i++){
7314             var c = this.config[i];
7315             if(fn.call(scope||this, c, i) === true){
7316                 r[r.length] = c;
7317             }
7318         }
7319         return r;
7320     },
7321
7322     /**
7323      * Returns true if the specified column is sortable.
7324      * @param {Number} col The column index
7325      * @return {Boolean}
7326      */
7327     isSortable : function(col){
7328         if(typeof this.config[col].sortable == "undefined"){
7329             return this.defaultSortable;
7330         }
7331         return this.config[col].sortable;
7332     },
7333
7334     /**
7335      * Returns the rendering (formatting) function defined for the column.
7336      * @param {Number} col The column index.
7337      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7338      */
7339     getRenderer : function(col){
7340         if(!this.config[col].renderer){
7341             return Roo.grid.ColumnModel.defaultRenderer;
7342         }
7343         return this.config[col].renderer;
7344     },
7345
7346     /**
7347      * Sets the rendering (formatting) function for a column.
7348      * @param {Number} col The column index
7349      * @param {Function} fn The function to use to process the cell's raw data
7350      * to return HTML markup for the grid view. The render function is called with
7351      * the following parameters:<ul>
7352      * <li>Data value.</li>
7353      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7354      * <li>css A CSS style string to apply to the table cell.</li>
7355      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7356      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7357      * <li>Row index</li>
7358      * <li>Column index</li>
7359      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7360      */
7361     setRenderer : function(col, fn){
7362         this.config[col].renderer = fn;
7363     },
7364
7365     /**
7366      * Returns the width for the specified column.
7367      * @param {Number} col The column index
7368      * @return {Number}
7369      */
7370     getColumnWidth : function(col){
7371         return this.config[col].width * 1 || this.defaultWidth;
7372     },
7373
7374     /**
7375      * Sets the width for a column.
7376      * @param {Number} col The column index
7377      * @param {Number} width The new width
7378      */
7379     setColumnWidth : function(col, width, suppressEvent){
7380         this.config[col].width = width;
7381         this.totalWidth = null;
7382         if(!suppressEvent){
7383              this.fireEvent("widthchange", this, col, width);
7384         }
7385     },
7386
7387     /**
7388      * Returns the total width of all columns.
7389      * @param {Boolean} includeHidden True to include hidden column widths
7390      * @return {Number}
7391      */
7392     getTotalWidth : function(includeHidden){
7393         if(!this.totalWidth){
7394             this.totalWidth = 0;
7395             for(var i = 0, len = this.config.length; i < len; i++){
7396                 if(includeHidden || !this.isHidden(i)){
7397                     this.totalWidth += this.getColumnWidth(i);
7398                 }
7399             }
7400         }
7401         return this.totalWidth;
7402     },
7403
7404     /**
7405      * Returns the header for the specified column.
7406      * @param {Number} col The column index
7407      * @return {String}
7408      */
7409     getColumnHeader : function(col){
7410         return this.config[col].header;
7411     },
7412
7413     /**
7414      * Sets the header for a column.
7415      * @param {Number} col The column index
7416      * @param {String} header The new header
7417      */
7418     setColumnHeader : function(col, header){
7419         this.config[col].header = header;
7420         this.fireEvent("headerchange", this, col, header);
7421     },
7422
7423     /**
7424      * Returns the tooltip for the specified column.
7425      * @param {Number} col The column index
7426      * @return {String}
7427      */
7428     getColumnTooltip : function(col){
7429             return this.config[col].tooltip;
7430     },
7431     /**
7432      * Sets the tooltip for a column.
7433      * @param {Number} col The column index
7434      * @param {String} tooltip The new tooltip
7435      */
7436     setColumnTooltip : function(col, tooltip){
7437             this.config[col].tooltip = tooltip;
7438     },
7439
7440     /**
7441      * Returns the dataIndex for the specified column.
7442      * @param {Number} col The column index
7443      * @return {Number}
7444      */
7445     getDataIndex : function(col){
7446         return this.config[col].dataIndex;
7447     },
7448
7449     /**
7450      * Sets the dataIndex for a column.
7451      * @param {Number} col The column index
7452      * @param {Number} dataIndex The new dataIndex
7453      */
7454     setDataIndex : function(col, dataIndex){
7455         this.config[col].dataIndex = dataIndex;
7456     },
7457
7458     
7459     
7460     /**
7461      * Returns true if the cell is editable.
7462      * @param {Number} colIndex The column index
7463      * @param {Number} rowIndex The row index - this is nto actually used..?
7464      * @return {Boolean}
7465      */
7466     isCellEditable : function(colIndex, rowIndex){
7467         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7468     },
7469
7470     /**
7471      * Returns the editor defined for the cell/column.
7472      * return false or null to disable editing.
7473      * @param {Number} colIndex The column index
7474      * @param {Number} rowIndex The row index
7475      * @return {Object}
7476      */
7477     getCellEditor : function(colIndex, rowIndex){
7478         return this.config[colIndex].editor;
7479     },
7480
7481     /**
7482      * Sets if a column is editable.
7483      * @param {Number} col The column index
7484      * @param {Boolean} editable True if the column is editable
7485      */
7486     setEditable : function(col, editable){
7487         this.config[col].editable = editable;
7488     },
7489
7490
7491     /**
7492      * Returns true if the column is hidden.
7493      * @param {Number} colIndex The column index
7494      * @return {Boolean}
7495      */
7496     isHidden : function(colIndex){
7497         return this.config[colIndex].hidden;
7498     },
7499
7500
7501     /**
7502      * Returns true if the column width cannot be changed
7503      */
7504     isFixed : function(colIndex){
7505         return this.config[colIndex].fixed;
7506     },
7507
7508     /**
7509      * Returns true if the column can be resized
7510      * @return {Boolean}
7511      */
7512     isResizable : function(colIndex){
7513         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7514     },
7515     /**
7516      * Sets if a column is hidden.
7517      * @param {Number} colIndex The column index
7518      * @param {Boolean} hidden True if the column is hidden
7519      */
7520     setHidden : function(colIndex, hidden){
7521         this.config[colIndex].hidden = hidden;
7522         this.totalWidth = null;
7523         this.fireEvent("hiddenchange", this, colIndex, hidden);
7524     },
7525
7526     /**
7527      * Sets the editor for a column.
7528      * @param {Number} col The column index
7529      * @param {Object} editor The editor object
7530      */
7531     setEditor : function(col, editor){
7532         this.config[col].editor = editor;
7533     }
7534 });
7535
7536 Roo.grid.ColumnModel.defaultRenderer = function(value)
7537 {
7538     if(typeof value == "object") {
7539         return value;
7540     }
7541         if(typeof value == "string" && value.length < 1){
7542             return "&#160;";
7543         }
7544     
7545         return String.format("{0}", value);
7546 };
7547
7548 // Alias for backwards compatibility
7549 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7550 /*
7551  * Based on:
7552  * Ext JS Library 1.1.1
7553  * Copyright(c) 2006-2007, Ext JS, LLC.
7554  *
7555  * Originally Released Under LGPL - original licence link has changed is not relivant.
7556  *
7557  * Fork - LGPL
7558  * <script type="text/javascript">
7559  */
7560  
7561 /**
7562  * @class Roo.LoadMask
7563  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7564  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7565  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7566  * element's UpdateManager load indicator and will be destroyed after the initial load.
7567  * @constructor
7568  * Create a new LoadMask
7569  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7570  * @param {Object} config The config object
7571  */
7572 Roo.LoadMask = function(el, config){
7573     this.el = Roo.get(el);
7574     Roo.apply(this, config);
7575     if(this.store){
7576         this.store.on('beforeload', this.onBeforeLoad, this);
7577         this.store.on('load', this.onLoad, this);
7578         this.store.on('loadexception', this.onLoadException, this);
7579         this.removeMask = false;
7580     }else{
7581         var um = this.el.getUpdateManager();
7582         um.showLoadIndicator = false; // disable the default indicator
7583         um.on('beforeupdate', this.onBeforeLoad, this);
7584         um.on('update', this.onLoad, this);
7585         um.on('failure', this.onLoad, this);
7586         this.removeMask = true;
7587     }
7588 };
7589
7590 Roo.LoadMask.prototype = {
7591     /**
7592      * @cfg {Boolean} removeMask
7593      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7594      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7595      */
7596     /**
7597      * @cfg {String} msg
7598      * The text to display in a centered loading message box (defaults to 'Loading...')
7599      */
7600     msg : 'Loading...',
7601     /**
7602      * @cfg {String} msgCls
7603      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7604      */
7605     msgCls : 'x-mask-loading',
7606
7607     /**
7608      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7609      * @type Boolean
7610      */
7611     disabled: false,
7612
7613     /**
7614      * Disables the mask to prevent it from being displayed
7615      */
7616     disable : function(){
7617        this.disabled = true;
7618     },
7619
7620     /**
7621      * Enables the mask so that it can be displayed
7622      */
7623     enable : function(){
7624         this.disabled = false;
7625     },
7626     
7627     onLoadException : function()
7628     {
7629         Roo.log(arguments);
7630         
7631         if (typeof(arguments[3]) != 'undefined') {
7632             Roo.MessageBox.alert("Error loading",arguments[3]);
7633         } 
7634         /*
7635         try {
7636             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7637                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7638             }   
7639         } catch(e) {
7640             
7641         }
7642         */
7643     
7644         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7645     },
7646     // private
7647     onLoad : function()
7648     {
7649         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7650     },
7651
7652     // private
7653     onBeforeLoad : function(){
7654         if(!this.disabled){
7655             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7656         }
7657     },
7658
7659     // private
7660     destroy : function(){
7661         if(this.store){
7662             this.store.un('beforeload', this.onBeforeLoad, this);
7663             this.store.un('load', this.onLoad, this);
7664             this.store.un('loadexception', this.onLoadException, this);
7665         }else{
7666             var um = this.el.getUpdateManager();
7667             um.un('beforeupdate', this.onBeforeLoad, this);
7668             um.un('update', this.onLoad, this);
7669             um.un('failure', this.onLoad, this);
7670         }
7671     }
7672 };/*
7673  * - LGPL
7674  *
7675  * table
7676  * 
7677  */
7678
7679 /**
7680  * @class Roo.bootstrap.Table
7681  * @extends Roo.bootstrap.Component
7682  * Bootstrap Table class
7683  * @cfg {String} cls table class
7684  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7685  * @cfg {String} bgcolor Specifies the background color for a table
7686  * @cfg {Number} border Specifies whether the table cells should have borders or not
7687  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7688  * @cfg {Number} cellspacing Specifies the space between cells
7689  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7690  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7691  * @cfg {String} sortable Specifies that the table should be sortable
7692  * @cfg {String} summary Specifies a summary of the content of a table
7693  * @cfg {Number} width Specifies the width of a table
7694  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7695  * 
7696  * @cfg {boolean} striped Should the rows be alternative striped
7697  * @cfg {boolean} bordered Add borders to the table
7698  * @cfg {boolean} hover Add hover highlighting
7699  * @cfg {boolean} condensed Format condensed
7700  * @cfg {boolean} responsive Format condensed
7701  * @cfg {Boolean} loadMask (true|false) default false
7702  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7703  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7704  * @cfg {Boolean} rowSelection (true|false) default false
7705  * @cfg {Boolean} cellSelection (true|false) default false
7706  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7707  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7708  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7709  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7710  
7711  * 
7712  * @constructor
7713  * Create a new Table
7714  * @param {Object} config The config object
7715  */
7716
7717 Roo.bootstrap.Table = function(config){
7718     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7719     
7720   
7721     
7722     // BC...
7723     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7724     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7725     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7726     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7727     
7728     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7729     if (this.sm) {
7730         this.sm.grid = this;
7731         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7732         this.sm = this.selModel;
7733         this.sm.xmodule = this.xmodule || false;
7734     }
7735     
7736     if (this.cm && typeof(this.cm.config) == 'undefined') {
7737         this.colModel = new Roo.grid.ColumnModel(this.cm);
7738         this.cm = this.colModel;
7739         this.cm.xmodule = this.xmodule || false;
7740     }
7741     if (this.store) {
7742         this.store= Roo.factory(this.store, Roo.data);
7743         this.ds = this.store;
7744         this.ds.xmodule = this.xmodule || false;
7745          
7746     }
7747     if (this.footer && this.store) {
7748         this.footer.dataSource = this.ds;
7749         this.footer = Roo.factory(this.footer);
7750     }
7751     
7752     /** @private */
7753     this.addEvents({
7754         /**
7755          * @event cellclick
7756          * Fires when a cell is clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Number} columnIndex
7761          * @param {Roo.EventObject} e
7762          */
7763         "cellclick" : true,
7764         /**
7765          * @event celldblclick
7766          * Fires when a cell is double clicked
7767          * @param {Roo.bootstrap.Table} this
7768          * @param {Roo.Element} el
7769          * @param {Number} rowIndex
7770          * @param {Number} columnIndex
7771          * @param {Roo.EventObject} e
7772          */
7773         "celldblclick" : true,
7774         /**
7775          * @event rowclick
7776          * Fires when a row is clicked
7777          * @param {Roo.bootstrap.Table} this
7778          * @param {Roo.Element} el
7779          * @param {Number} rowIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "rowclick" : true,
7783         /**
7784          * @event rowdblclick
7785          * Fires when a row is double clicked
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Roo.Element} el
7788          * @param {Number} rowIndex
7789          * @param {Roo.EventObject} e
7790          */
7791         "rowdblclick" : true,
7792         /**
7793          * @event mouseover
7794          * Fires when a mouseover occur
7795          * @param {Roo.bootstrap.Table} this
7796          * @param {Roo.Element} el
7797          * @param {Number} rowIndex
7798          * @param {Number} columnIndex
7799          * @param {Roo.EventObject} e
7800          */
7801         "mouseover" : true,
7802         /**
7803          * @event mouseout
7804          * Fires when a mouseout occur
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Roo.Element} el
7807          * @param {Number} rowIndex
7808          * @param {Number} columnIndex
7809          * @param {Roo.EventObject} e
7810          */
7811         "mouseout" : true,
7812         /**
7813          * @event rowclass
7814          * Fires when a row is rendered, so you can change add a style to it.
7815          * @param {Roo.bootstrap.Table} this
7816          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7817          */
7818         'rowclass' : true,
7819           /**
7820          * @event rowsrendered
7821          * Fires when all the  rows have been rendered
7822          * @param {Roo.bootstrap.Table} this
7823          */
7824         'rowsrendered' : true,
7825         /**
7826          * @event contextmenu
7827          * The raw contextmenu event for the entire grid.
7828          * @param {Roo.EventObject} e
7829          */
7830         "contextmenu" : true,
7831         /**
7832          * @event rowcontextmenu
7833          * Fires when a row is right clicked
7834          * @param {Roo.bootstrap.Table} this
7835          * @param {Number} rowIndex
7836          * @param {Roo.EventObject} e
7837          */
7838         "rowcontextmenu" : true,
7839         /**
7840          * @event cellcontextmenu
7841          * Fires when a cell is right clicked
7842          * @param {Roo.bootstrap.Table} this
7843          * @param {Number} rowIndex
7844          * @param {Number} cellIndex
7845          * @param {Roo.EventObject} e
7846          */
7847          "cellcontextmenu" : true,
7848          /**
7849          * @event headercontextmenu
7850          * Fires when a header is right clicked
7851          * @param {Roo.bootstrap.Table} this
7852          * @param {Number} columnIndex
7853          * @param {Roo.EventObject} e
7854          */
7855         "headercontextmenu" : true
7856     });
7857 };
7858
7859 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7860     
7861     cls: false,
7862     align: false,
7863     bgcolor: false,
7864     border: false,
7865     cellpadding: false,
7866     cellspacing: false,
7867     frame: false,
7868     rules: false,
7869     sortable: false,
7870     summary: false,
7871     width: false,
7872     striped : false,
7873     scrollBody : false,
7874     bordered: false,
7875     hover:  false,
7876     condensed : false,
7877     responsive : false,
7878     sm : false,
7879     cm : false,
7880     store : false,
7881     loadMask : false,
7882     footerShow : true,
7883     headerShow : true,
7884   
7885     rowSelection : false,
7886     cellSelection : false,
7887     layout : false,
7888     
7889     // Roo.Element - the tbody
7890     mainBody: false,
7891     // Roo.Element - thead element
7892     mainHead: false,
7893     
7894     container: false, // used by gridpanel...
7895     
7896     lazyLoad : false,
7897     
7898     CSS : Roo.util.CSS,
7899     
7900     auto_hide_footer : false,
7901     
7902     getAutoCreate : function()
7903     {
7904         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7905         
7906         cfg = {
7907             tag: 'table',
7908             cls : 'table',
7909             cn : []
7910         };
7911         if (this.scrollBody) {
7912             cfg.cls += ' table-body-fixed';
7913         }    
7914         if (this.striped) {
7915             cfg.cls += ' table-striped';
7916         }
7917         
7918         if (this.hover) {
7919             cfg.cls += ' table-hover';
7920         }
7921         if (this.bordered) {
7922             cfg.cls += ' table-bordered';
7923         }
7924         if (this.condensed) {
7925             cfg.cls += ' table-condensed';
7926         }
7927         if (this.responsive) {
7928             cfg.cls += ' table-responsive';
7929         }
7930         
7931         if (this.cls) {
7932             cfg.cls+=  ' ' +this.cls;
7933         }
7934         
7935         // this lot should be simplifed...
7936         var _t = this;
7937         var cp = [
7938             'align',
7939             'bgcolor',
7940             'border',
7941             'cellpadding',
7942             'cellspacing',
7943             'frame',
7944             'rules',
7945             'sortable',
7946             'summary',
7947             'width'
7948         ].forEach(function(k) {
7949             if (_t[k]) {
7950                 cfg[k] = _t[k];
7951             }
7952         });
7953         
7954         
7955         if (this.layout) {
7956             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7957         }
7958         
7959         if(this.store || this.cm){
7960             if(this.headerShow){
7961                 cfg.cn.push(this.renderHeader());
7962             }
7963             
7964             cfg.cn.push(this.renderBody());
7965             
7966             if(this.footerShow){
7967                 cfg.cn.push(this.renderFooter());
7968             }
7969             // where does this come from?
7970             //cfg.cls+=  ' TableGrid';
7971         }
7972         
7973         return { cn : [ cfg ] };
7974     },
7975     
7976     initEvents : function()
7977     {   
7978         if(!this.store || !this.cm){
7979             return;
7980         }
7981         if (this.selModel) {
7982             this.selModel.initEvents();
7983         }
7984         
7985         
7986         //Roo.log('initEvents with ds!!!!');
7987         
7988         this.mainBody = this.el.select('tbody', true).first();
7989         this.mainHead = this.el.select('thead', true).first();
7990         this.mainFoot = this.el.select('tfoot', true).first();
7991         
7992         
7993         
7994         var _this = this;
7995         
7996         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7997             e.on('click', _this.sort, _this);
7998         });
7999         
8000         this.mainBody.on("click", this.onClick, this);
8001         this.mainBody.on("dblclick", this.onDblClick, this);
8002         
8003         // why is this done????? = it breaks dialogs??
8004         //this.parent().el.setStyle('position', 'relative');
8005         
8006         
8007         if (this.footer) {
8008             this.footer.parentId = this.id;
8009             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8010             
8011             if(this.lazyLoad){
8012                 this.el.select('tfoot tr td').first().addClass('hide');
8013             }
8014         } 
8015         
8016         if(this.loadMask) {
8017             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8018         }
8019         
8020         this.store.on('load', this.onLoad, this);
8021         this.store.on('beforeload', this.onBeforeLoad, this);
8022         this.store.on('update', this.onUpdate, this);
8023         this.store.on('add', this.onAdd, this);
8024         this.store.on("clear", this.clear, this);
8025         
8026         this.el.on("contextmenu", this.onContextMenu, this);
8027         
8028         this.mainBody.on('scroll', this.onBodyScroll, this);
8029         
8030         this.cm.on("headerchange", this.onHeaderChange, this);
8031         
8032         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8033         
8034     },
8035     
8036     onContextMenu : function(e, t)
8037     {
8038         this.processEvent("contextmenu", e);
8039     },
8040     
8041     processEvent : function(name, e)
8042     {
8043         if (name != 'touchstart' ) {
8044             this.fireEvent(name, e);    
8045         }
8046         
8047         var t = e.getTarget();
8048         
8049         var cell = Roo.get(t);
8050         
8051         if(!cell){
8052             return;
8053         }
8054         
8055         if(cell.findParent('tfoot', false, true)){
8056             return;
8057         }
8058         
8059         if(cell.findParent('thead', false, true)){
8060             
8061             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8062                 cell = Roo.get(t).findParent('th', false, true);
8063                 if (!cell) {
8064                     Roo.log("failed to find th in thead?");
8065                     Roo.log(e.getTarget());
8066                     return;
8067                 }
8068             }
8069             
8070             var cellIndex = cell.dom.cellIndex;
8071             
8072             var ename = name == 'touchstart' ? 'click' : name;
8073             this.fireEvent("header" + ename, this, cellIndex, e);
8074             
8075             return;
8076         }
8077         
8078         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8079             cell = Roo.get(t).findParent('td', false, true);
8080             if (!cell) {
8081                 Roo.log("failed to find th in tbody?");
8082                 Roo.log(e.getTarget());
8083                 return;
8084             }
8085         }
8086         
8087         var row = cell.findParent('tr', false, true);
8088         var cellIndex = cell.dom.cellIndex;
8089         var rowIndex = row.dom.rowIndex - 1;
8090         
8091         if(row !== false){
8092             
8093             this.fireEvent("row" + name, this, rowIndex, e);
8094             
8095             if(cell !== false){
8096             
8097                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8098             }
8099         }
8100         
8101     },
8102     
8103     onMouseover : function(e, el)
8104     {
8105         var cell = Roo.get(el);
8106         
8107         if(!cell){
8108             return;
8109         }
8110         
8111         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8112             cell = cell.findParent('td', false, true);
8113         }
8114         
8115         var row = cell.findParent('tr', false, true);
8116         var cellIndex = cell.dom.cellIndex;
8117         var rowIndex = row.dom.rowIndex - 1; // start from 0
8118         
8119         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8120         
8121     },
8122     
8123     onMouseout : function(e, el)
8124     {
8125         var cell = Roo.get(el);
8126         
8127         if(!cell){
8128             return;
8129         }
8130         
8131         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8132             cell = cell.findParent('td', false, true);
8133         }
8134         
8135         var row = cell.findParent('tr', false, true);
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = row.dom.rowIndex - 1; // start from 0
8138         
8139         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8140         
8141     },
8142     
8143     onClick : function(e, el)
8144     {
8145         var cell = Roo.get(el);
8146         
8147         if(!cell || (!this.cellSelection && !this.rowSelection)){
8148             return;
8149         }
8150         
8151         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8152             cell = cell.findParent('td', false, true);
8153         }
8154         
8155         if(!cell || typeof(cell) == 'undefined'){
8156             return;
8157         }
8158         
8159         var row = cell.findParent('tr', false, true);
8160         
8161         if(!row || typeof(row) == 'undefined'){
8162             return;
8163         }
8164         
8165         var cellIndex = cell.dom.cellIndex;
8166         var rowIndex = this.getRowIndex(row);
8167         
8168         // why??? - should these not be based on SelectionModel?
8169         if(this.cellSelection){
8170             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8171         }
8172         
8173         if(this.rowSelection){
8174             this.fireEvent('rowclick', this, row, rowIndex, e);
8175         }
8176         
8177         
8178     },
8179         
8180     onDblClick : function(e,el)
8181     {
8182         var cell = Roo.get(el);
8183         
8184         if(!cell || (!this.cellSelection && !this.rowSelection)){
8185             return;
8186         }
8187         
8188         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8189             cell = cell.findParent('td', false, true);
8190         }
8191         
8192         if(!cell || typeof(cell) == 'undefined'){
8193             return;
8194         }
8195         
8196         var row = cell.findParent('tr', false, true);
8197         
8198         if(!row || typeof(row) == 'undefined'){
8199             return;
8200         }
8201         
8202         var cellIndex = cell.dom.cellIndex;
8203         var rowIndex = this.getRowIndex(row);
8204         
8205         if(this.cellSelection){
8206             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8207         }
8208         
8209         if(this.rowSelection){
8210             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8211         }
8212     },
8213     
8214     sort : function(e,el)
8215     {
8216         var col = Roo.get(el);
8217         
8218         if(!col.hasClass('sortable')){
8219             return;
8220         }
8221         
8222         var sort = col.attr('sort');
8223         var dir = 'ASC';
8224         
8225         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8226             dir = 'DESC';
8227         }
8228         
8229         this.store.sortInfo = {field : sort, direction : dir};
8230         
8231         if (this.footer) {
8232             Roo.log("calling footer first");
8233             this.footer.onClick('first');
8234         } else {
8235         
8236             this.store.load({ params : { start : 0 } });
8237         }
8238     },
8239     
8240     renderHeader : function()
8241     {
8242         var header = {
8243             tag: 'thead',
8244             cn : []
8245         };
8246         
8247         var cm = this.cm;
8248         this.totalWidth = 0;
8249         
8250         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8251             
8252             var config = cm.config[i];
8253             
8254             var c = {
8255                 tag: 'th',
8256                 cls : 'x-hcol-' + i,
8257                 style : '',
8258                 html: cm.getColumnHeader(i)
8259             };
8260             
8261             var hh = '';
8262             
8263             if(typeof(config.sortable) != 'undefined' && config.sortable){
8264                 c.cls = 'sortable';
8265                 c.html = '<i class="glyphicon"></i>' + c.html;
8266             }
8267             
8268             // could use BS4 hidden-..-down 
8269             
8270             if(typeof(config.lgHeader) != 'undefined'){
8271                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8272             }
8273             
8274             if(typeof(config.mdHeader) != 'undefined'){
8275                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8276             }
8277             
8278             if(typeof(config.smHeader) != 'undefined'){
8279                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8280             }
8281             
8282             if(typeof(config.xsHeader) != 'undefined'){
8283                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8284             }
8285             
8286             if(hh.length){
8287                 c.html = hh;
8288             }
8289             
8290             if(typeof(config.tooltip) != 'undefined'){
8291                 c.tooltip = config.tooltip;
8292             }
8293             
8294             if(typeof(config.colspan) != 'undefined'){
8295                 c.colspan = config.colspan;
8296             }
8297             
8298             if(typeof(config.hidden) != 'undefined' && config.hidden){
8299                 c.style += ' display:none;';
8300             }
8301             
8302             if(typeof(config.dataIndex) != 'undefined'){
8303                 c.sort = config.dataIndex;
8304             }
8305             
8306            
8307             
8308             if(typeof(config.align) != 'undefined' && config.align.length){
8309                 c.style += ' text-align:' + config.align + ';';
8310             }
8311             
8312             if(typeof(config.width) != 'undefined'){
8313                 c.style += ' width:' + config.width + 'px;';
8314                 this.totalWidth += config.width;
8315             } else {
8316                 this.totalWidth += 100; // assume minimum of 100 per column?
8317             }
8318             
8319             if(typeof(config.cls) != 'undefined'){
8320                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8321             }
8322             
8323             ['xs','sm','md','lg'].map(function(size){
8324                 
8325                 if(typeof(config[size]) == 'undefined'){
8326                     return;
8327                 }
8328                  
8329                 if (!config[size]) { // 0 = hidden
8330                     // BS 4 '0' is treated as hide that column and below.
8331                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8332                     return;
8333                 }
8334                 
8335                 c.cls += ' col-' + size + '-' + config[size] + (
8336                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8337                 );
8338                 
8339                 
8340             });
8341             
8342             header.cn.push(c)
8343         }
8344         
8345         return header;
8346     },
8347     
8348     renderBody : function()
8349     {
8350         var body = {
8351             tag: 'tbody',
8352             cn : [
8353                 {
8354                     tag: 'tr',
8355                     cn : [
8356                         {
8357                             tag : 'td',
8358                             colspan :  this.cm.getColumnCount()
8359                         }
8360                     ]
8361                 }
8362             ]
8363         };
8364         
8365         return body;
8366     },
8367     
8368     renderFooter : function()
8369     {
8370         var footer = {
8371             tag: 'tfoot',
8372             cn : [
8373                 {
8374                     tag: 'tr',
8375                     cn : [
8376                         {
8377                             tag : 'td',
8378                             colspan :  this.cm.getColumnCount()
8379                         }
8380                     ]
8381                 }
8382             ]
8383         };
8384         
8385         return footer;
8386     },
8387     
8388     
8389     
8390     onLoad : function()
8391     {
8392 //        Roo.log('ds onload');
8393         this.clear();
8394         
8395         var _this = this;
8396         var cm = this.cm;
8397         var ds = this.store;
8398         
8399         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8400             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8401             if (_this.store.sortInfo) {
8402                     
8403                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8404                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8405                 }
8406                 
8407                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8408                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8409                 }
8410             }
8411         });
8412         
8413         var tbody =  this.mainBody;
8414               
8415         if(ds.getCount() > 0){
8416             ds.data.each(function(d,rowIndex){
8417                 var row =  this.renderRow(cm, ds, rowIndex);
8418                 
8419                 tbody.createChild(row);
8420                 
8421                 var _this = this;
8422                 
8423                 if(row.cellObjects.length){
8424                     Roo.each(row.cellObjects, function(r){
8425                         _this.renderCellObject(r);
8426                     })
8427                 }
8428                 
8429             }, this);
8430         }
8431         
8432         var tfoot = this.el.select('tfoot', true).first();
8433         
8434         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8435             
8436             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8437             
8438             var total = this.ds.getTotalCount();
8439             
8440             if(this.footer.pageSize < total){
8441                 this.mainFoot.show();
8442             }
8443         }
8444         
8445         Roo.each(this.el.select('tbody td', true).elements, function(e){
8446             e.on('mouseover', _this.onMouseover, _this);
8447         });
8448         
8449         Roo.each(this.el.select('tbody td', true).elements, function(e){
8450             e.on('mouseout', _this.onMouseout, _this);
8451         });
8452         this.fireEvent('rowsrendered', this);
8453         
8454         this.autoSize();
8455     },
8456     
8457     
8458     onUpdate : function(ds,record)
8459     {
8460         this.refreshRow(record);
8461         this.autoSize();
8462     },
8463     
8464     onRemove : function(ds, record, index, isUpdate){
8465         if(isUpdate !== true){
8466             this.fireEvent("beforerowremoved", this, index, record);
8467         }
8468         var bt = this.mainBody.dom;
8469         
8470         var rows = this.el.select('tbody > tr', true).elements;
8471         
8472         if(typeof(rows[index]) != 'undefined'){
8473             bt.removeChild(rows[index].dom);
8474         }
8475         
8476 //        if(bt.rows[index]){
8477 //            bt.removeChild(bt.rows[index]);
8478 //        }
8479         
8480         if(isUpdate !== true){
8481             //this.stripeRows(index);
8482             //this.syncRowHeights(index, index);
8483             //this.layout();
8484             this.fireEvent("rowremoved", this, index, record);
8485         }
8486     },
8487     
8488     onAdd : function(ds, records, rowIndex)
8489     {
8490         //Roo.log('on Add called');
8491         // - note this does not handle multiple adding very well..
8492         var bt = this.mainBody.dom;
8493         for (var i =0 ; i < records.length;i++) {
8494             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8495             //Roo.log(records[i]);
8496             //Roo.log(this.store.getAt(rowIndex+i));
8497             this.insertRow(this.store, rowIndex + i, false);
8498             return;
8499         }
8500         
8501     },
8502     
8503     
8504     refreshRow : function(record){
8505         var ds = this.store, index;
8506         if(typeof record == 'number'){
8507             index = record;
8508             record = ds.getAt(index);
8509         }else{
8510             index = ds.indexOf(record);
8511             if (index < 0) {
8512                 return; // should not happen - but seems to 
8513             }
8514         }
8515         this.insertRow(ds, index, true);
8516         this.autoSize();
8517         this.onRemove(ds, record, index+1, true);
8518         this.autoSize();
8519         //this.syncRowHeights(index, index);
8520         //this.layout();
8521         this.fireEvent("rowupdated", this, index, record);
8522     },
8523     
8524     insertRow : function(dm, rowIndex, isUpdate){
8525         
8526         if(!isUpdate){
8527             this.fireEvent("beforerowsinserted", this, rowIndex);
8528         }
8529             //var s = this.getScrollState();
8530         var row = this.renderRow(this.cm, this.store, rowIndex);
8531         // insert before rowIndex..
8532         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8533         
8534         var _this = this;
8535                 
8536         if(row.cellObjects.length){
8537             Roo.each(row.cellObjects, function(r){
8538                 _this.renderCellObject(r);
8539             })
8540         }
8541             
8542         if(!isUpdate){
8543             this.fireEvent("rowsinserted", this, rowIndex);
8544             //this.syncRowHeights(firstRow, lastRow);
8545             //this.stripeRows(firstRow);
8546             //this.layout();
8547         }
8548         
8549     },
8550     
8551     
8552     getRowDom : function(rowIndex)
8553     {
8554         var rows = this.el.select('tbody > tr', true).elements;
8555         
8556         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8557         
8558     },
8559     // returns the object tree for a tr..
8560   
8561     
8562     renderRow : function(cm, ds, rowIndex) 
8563     {
8564         var d = ds.getAt(rowIndex);
8565         
8566         var row = {
8567             tag : 'tr',
8568             cls : 'x-row-' + rowIndex,
8569             cn : []
8570         };
8571             
8572         var cellObjects = [];
8573         
8574         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8575             var config = cm.config[i];
8576             
8577             var renderer = cm.getRenderer(i);
8578             var value = '';
8579             var id = false;
8580             
8581             if(typeof(renderer) !== 'undefined'){
8582                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8583             }
8584             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8585             // and are rendered into the cells after the row is rendered - using the id for the element.
8586             
8587             if(typeof(value) === 'object'){
8588                 id = Roo.id();
8589                 cellObjects.push({
8590                     container : id,
8591                     cfg : value 
8592                 })
8593             }
8594             
8595             var rowcfg = {
8596                 record: d,
8597                 rowIndex : rowIndex,
8598                 colIndex : i,
8599                 rowClass : ''
8600             };
8601
8602             this.fireEvent('rowclass', this, rowcfg);
8603             
8604             var td = {
8605                 tag: 'td',
8606                 cls : rowcfg.rowClass + ' x-col-' + i,
8607                 style: '',
8608                 html: (typeof(value) === 'object') ? '' : value
8609             };
8610             
8611             if (id) {
8612                 td.id = id;
8613             }
8614             
8615             if(typeof(config.colspan) != 'undefined'){
8616                 td.colspan = config.colspan;
8617             }
8618             
8619             if(typeof(config.hidden) != 'undefined' && config.hidden){
8620                 td.style += ' display:none;';
8621             }
8622             
8623             if(typeof(config.align) != 'undefined' && config.align.length){
8624                 td.style += ' text-align:' + config.align + ';';
8625             }
8626             if(typeof(config.valign) != 'undefined' && config.valign.length){
8627                 td.style += ' vertical-align:' + config.valign + ';';
8628             }
8629             
8630             if(typeof(config.width) != 'undefined'){
8631                 td.style += ' width:' +  config.width + 'px;';
8632             }
8633             
8634             if(typeof(config.cursor) != 'undefined'){
8635                 td.style += ' cursor:' +  config.cursor + ';';
8636             }
8637             
8638             if(typeof(config.cls) != 'undefined'){
8639                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8640             }
8641             
8642             ['xs','sm','md','lg'].map(function(size){
8643                 
8644                 if(typeof(config[size]) == 'undefined'){
8645                     return;
8646                 }
8647                 
8648                 
8649                   
8650                 if (!config[size]) { // 0 = hidden
8651                     // BS 4 '0' is treated as hide that column and below.
8652                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8653                     return;
8654                 }
8655                 
8656                 td.cls += ' col-' + size + '-' + config[size] + (
8657                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8658                 );
8659                  
8660
8661             });
8662             
8663             row.cn.push(td);
8664            
8665         }
8666         
8667         row.cellObjects = cellObjects;
8668         
8669         return row;
8670           
8671     },
8672     
8673     
8674     
8675     onBeforeLoad : function()
8676     {
8677         
8678     },
8679      /**
8680      * Remove all rows
8681      */
8682     clear : function()
8683     {
8684         this.el.select('tbody', true).first().dom.innerHTML = '';
8685     },
8686     /**
8687      * Show or hide a row.
8688      * @param {Number} rowIndex to show or hide
8689      * @param {Boolean} state hide
8690      */
8691     setRowVisibility : function(rowIndex, state)
8692     {
8693         var bt = this.mainBody.dom;
8694         
8695         var rows = this.el.select('tbody > tr', true).elements;
8696         
8697         if(typeof(rows[rowIndex]) == 'undefined'){
8698             return;
8699         }
8700         rows[rowIndex].dom.style.display = state ? '' : 'none';
8701     },
8702     
8703     
8704     getSelectionModel : function(){
8705         if(!this.selModel){
8706             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8707         }
8708         return this.selModel;
8709     },
8710     /*
8711      * Render the Roo.bootstrap object from renderder
8712      */
8713     renderCellObject : function(r)
8714     {
8715         var _this = this;
8716         
8717         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8718         
8719         var t = r.cfg.render(r.container);
8720         
8721         if(r.cfg.cn){
8722             Roo.each(r.cfg.cn, function(c){
8723                 var child = {
8724                     container: t.getChildContainer(),
8725                     cfg: c
8726                 };
8727                 _this.renderCellObject(child);
8728             })
8729         }
8730     },
8731     
8732     getRowIndex : function(row)
8733     {
8734         var rowIndex = -1;
8735         
8736         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8737             if(el != row){
8738                 return;
8739             }
8740             
8741             rowIndex = index;
8742         });
8743         
8744         return rowIndex;
8745     },
8746      /**
8747      * Returns the grid's underlying element = used by panel.Grid
8748      * @return {Element} The element
8749      */
8750     getGridEl : function(){
8751         return this.el;
8752     },
8753      /**
8754      * Forces a resize - used by panel.Grid
8755      * @return {Element} The element
8756      */
8757     autoSize : function()
8758     {
8759         //var ctr = Roo.get(this.container.dom.parentElement);
8760         var ctr = Roo.get(this.el.dom);
8761         
8762         var thd = this.getGridEl().select('thead',true).first();
8763         var tbd = this.getGridEl().select('tbody', true).first();
8764         var tfd = this.getGridEl().select('tfoot', true).first();
8765         
8766         var cw = ctr.getWidth();
8767         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8768         
8769         if (tbd) {
8770             
8771             tbd.setWidth(ctr.getWidth());
8772             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8773             // this needs fixing for various usage - currently only hydra job advers I think..
8774             //tdb.setHeight(
8775             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8776             //); 
8777             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8778             cw -= barsize;
8779         }
8780         cw = Math.max(cw, this.totalWidth);
8781         this.getGridEl().select('tbody tr',true).setWidth(cw);
8782         
8783         // resize 'expandable coloumn?
8784         
8785         return; // we doe not have a view in this design..
8786         
8787     },
8788     onBodyScroll: function()
8789     {
8790         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8791         if(this.mainHead){
8792             this.mainHead.setStyle({
8793                 'position' : 'relative',
8794                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8795             });
8796         }
8797         
8798         if(this.lazyLoad){
8799             
8800             var scrollHeight = this.mainBody.dom.scrollHeight;
8801             
8802             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8803             
8804             var height = this.mainBody.getHeight();
8805             
8806             if(scrollHeight - height == scrollTop) {
8807                 
8808                 var total = this.ds.getTotalCount();
8809                 
8810                 if(this.footer.cursor + this.footer.pageSize < total){
8811                     
8812                     this.footer.ds.load({
8813                         params : {
8814                             start : this.footer.cursor + this.footer.pageSize,
8815                             limit : this.footer.pageSize
8816                         },
8817                         add : true
8818                     });
8819                 }
8820             }
8821             
8822         }
8823     },
8824     
8825     onHeaderChange : function()
8826     {
8827         var header = this.renderHeader();
8828         var table = this.el.select('table', true).first();
8829         
8830         this.mainHead.remove();
8831         this.mainHead = table.createChild(header, this.mainBody, false);
8832     },
8833     
8834     onHiddenChange : function(colModel, colIndex, hidden)
8835     {
8836         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8837         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8838         
8839         this.CSS.updateRule(thSelector, "display", "");
8840         this.CSS.updateRule(tdSelector, "display", "");
8841         
8842         if(hidden){
8843             this.CSS.updateRule(thSelector, "display", "none");
8844             this.CSS.updateRule(tdSelector, "display", "none");
8845         }
8846         
8847         this.onHeaderChange();
8848         this.onLoad();
8849     },
8850     
8851     setColumnWidth: function(col_index, width)
8852     {
8853         // width = "md-2 xs-2..."
8854         if(!this.colModel.config[col_index]) {
8855             return;
8856         }
8857         
8858         var w = width.split(" ");
8859         
8860         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8861         
8862         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8863         
8864         
8865         for(var j = 0; j < w.length; j++) {
8866             
8867             if(!w[j]) {
8868                 continue;
8869             }
8870             
8871             var size_cls = w[j].split("-");
8872             
8873             if(!Number.isInteger(size_cls[1] * 1)) {
8874                 continue;
8875             }
8876             
8877             if(!this.colModel.config[col_index][size_cls[0]]) {
8878                 continue;
8879             }
8880             
8881             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8882                 continue;
8883             }
8884             
8885             h_row[0].classList.replace(
8886                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8887                 "col-"+size_cls[0]+"-"+size_cls[1]
8888             );
8889             
8890             for(var i = 0; i < rows.length; i++) {
8891                 
8892                 var size_cls = w[j].split("-");
8893                 
8894                 if(!Number.isInteger(size_cls[1] * 1)) {
8895                     continue;
8896                 }
8897                 
8898                 if(!this.colModel.config[col_index][size_cls[0]]) {
8899                     continue;
8900                 }
8901                 
8902                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8903                     continue;
8904                 }
8905                 
8906                 rows[i].classList.replace(
8907                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8908                     "col-"+size_cls[0]+"-"+size_cls[1]
8909                 );
8910             }
8911             
8912             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8913         }
8914     }
8915 });
8916
8917  
8918
8919  /*
8920  * - LGPL
8921  *
8922  * table cell
8923  * 
8924  */
8925
8926 /**
8927  * @class Roo.bootstrap.TableCell
8928  * @extends Roo.bootstrap.Component
8929  * Bootstrap TableCell class
8930  * @cfg {String} html cell contain text
8931  * @cfg {String} cls cell class
8932  * @cfg {String} tag cell tag (td|th) default td
8933  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8934  * @cfg {String} align Aligns the content in a cell
8935  * @cfg {String} axis Categorizes cells
8936  * @cfg {String} bgcolor Specifies the background color of a cell
8937  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8938  * @cfg {Number} colspan Specifies the number of columns a cell should span
8939  * @cfg {String} headers Specifies one or more header cells a cell is related to
8940  * @cfg {Number} height Sets the height of a cell
8941  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8942  * @cfg {Number} rowspan Sets the number of rows a cell should span
8943  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8944  * @cfg {String} valign Vertical aligns the content in a cell
8945  * @cfg {Number} width Specifies the width of a cell
8946  * 
8947  * @constructor
8948  * Create a new TableCell
8949  * @param {Object} config The config object
8950  */
8951
8952 Roo.bootstrap.TableCell = function(config){
8953     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8954 };
8955
8956 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8957     
8958     html: false,
8959     cls: false,
8960     tag: false,
8961     abbr: false,
8962     align: false,
8963     axis: false,
8964     bgcolor: false,
8965     charoff: false,
8966     colspan: false,
8967     headers: false,
8968     height: false,
8969     nowrap: false,
8970     rowspan: false,
8971     scope: false,
8972     valign: false,
8973     width: false,
8974     
8975     
8976     getAutoCreate : function(){
8977         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8978         
8979         cfg = {
8980             tag: 'td'
8981         };
8982         
8983         if(this.tag){
8984             cfg.tag = this.tag;
8985         }
8986         
8987         if (this.html) {
8988             cfg.html=this.html
8989         }
8990         if (this.cls) {
8991             cfg.cls=this.cls
8992         }
8993         if (this.abbr) {
8994             cfg.abbr=this.abbr
8995         }
8996         if (this.align) {
8997             cfg.align=this.align
8998         }
8999         if (this.axis) {
9000             cfg.axis=this.axis
9001         }
9002         if (this.bgcolor) {
9003             cfg.bgcolor=this.bgcolor
9004         }
9005         if (this.charoff) {
9006             cfg.charoff=this.charoff
9007         }
9008         if (this.colspan) {
9009             cfg.colspan=this.colspan
9010         }
9011         if (this.headers) {
9012             cfg.headers=this.headers
9013         }
9014         if (this.height) {
9015             cfg.height=this.height
9016         }
9017         if (this.nowrap) {
9018             cfg.nowrap=this.nowrap
9019         }
9020         if (this.rowspan) {
9021             cfg.rowspan=this.rowspan
9022         }
9023         if (this.scope) {
9024             cfg.scope=this.scope
9025         }
9026         if (this.valign) {
9027             cfg.valign=this.valign
9028         }
9029         if (this.width) {
9030             cfg.width=this.width
9031         }
9032         
9033         
9034         return cfg;
9035     }
9036    
9037 });
9038
9039  
9040
9041  /*
9042  * - LGPL
9043  *
9044  * table row
9045  * 
9046  */
9047
9048 /**
9049  * @class Roo.bootstrap.TableRow
9050  * @extends Roo.bootstrap.Component
9051  * Bootstrap TableRow class
9052  * @cfg {String} cls row class
9053  * @cfg {String} align Aligns the content in a table row
9054  * @cfg {String} bgcolor Specifies a background color for a table row
9055  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9056  * @cfg {String} valign Vertical aligns the content in a table row
9057  * 
9058  * @constructor
9059  * Create a new TableRow
9060  * @param {Object} config The config object
9061  */
9062
9063 Roo.bootstrap.TableRow = function(config){
9064     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9065 };
9066
9067 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9068     
9069     cls: false,
9070     align: false,
9071     bgcolor: false,
9072     charoff: false,
9073     valign: false,
9074     
9075     getAutoCreate : function(){
9076         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9077         
9078         cfg = {
9079             tag: 'tr'
9080         };
9081             
9082         if(this.cls){
9083             cfg.cls = this.cls;
9084         }
9085         if(this.align){
9086             cfg.align = this.align;
9087         }
9088         if(this.bgcolor){
9089             cfg.bgcolor = this.bgcolor;
9090         }
9091         if(this.charoff){
9092             cfg.charoff = this.charoff;
9093         }
9094         if(this.valign){
9095             cfg.valign = this.valign;
9096         }
9097         
9098         return cfg;
9099     }
9100    
9101 });
9102
9103  
9104
9105  /*
9106  * - LGPL
9107  *
9108  * table body
9109  * 
9110  */
9111
9112 /**
9113  * @class Roo.bootstrap.TableBody
9114  * @extends Roo.bootstrap.Component
9115  * Bootstrap TableBody class
9116  * @cfg {String} cls element class
9117  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9118  * @cfg {String} align Aligns the content inside the element
9119  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9120  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9121  * 
9122  * @constructor
9123  * Create a new TableBody
9124  * @param {Object} config The config object
9125  */
9126
9127 Roo.bootstrap.TableBody = function(config){
9128     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9129 };
9130
9131 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9132     
9133     cls: false,
9134     tag: false,
9135     align: false,
9136     charoff: false,
9137     valign: false,
9138     
9139     getAutoCreate : function(){
9140         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9141         
9142         cfg = {
9143             tag: 'tbody'
9144         };
9145             
9146         if (this.cls) {
9147             cfg.cls=this.cls
9148         }
9149         if(this.tag){
9150             cfg.tag = this.tag;
9151         }
9152         
9153         if(this.align){
9154             cfg.align = this.align;
9155         }
9156         if(this.charoff){
9157             cfg.charoff = this.charoff;
9158         }
9159         if(this.valign){
9160             cfg.valign = this.valign;
9161         }
9162         
9163         return cfg;
9164     }
9165     
9166     
9167 //    initEvents : function()
9168 //    {
9169 //        
9170 //        if(!this.store){
9171 //            return;
9172 //        }
9173 //        
9174 //        this.store = Roo.factory(this.store, Roo.data);
9175 //        this.store.on('load', this.onLoad, this);
9176 //        
9177 //        this.store.load();
9178 //        
9179 //    },
9180 //    
9181 //    onLoad: function () 
9182 //    {   
9183 //        this.fireEvent('load', this);
9184 //    }
9185 //    
9186 //   
9187 });
9188
9189  
9190
9191  /*
9192  * Based on:
9193  * Ext JS Library 1.1.1
9194  * Copyright(c) 2006-2007, Ext JS, LLC.
9195  *
9196  * Originally Released Under LGPL - original licence link has changed is not relivant.
9197  *
9198  * Fork - LGPL
9199  * <script type="text/javascript">
9200  */
9201
9202 // as we use this in bootstrap.
9203 Roo.namespace('Roo.form');
9204  /**
9205  * @class Roo.form.Action
9206  * Internal Class used to handle form actions
9207  * @constructor
9208  * @param {Roo.form.BasicForm} el The form element or its id
9209  * @param {Object} config Configuration options
9210  */
9211
9212  
9213  
9214 // define the action interface
9215 Roo.form.Action = function(form, options){
9216     this.form = form;
9217     this.options = options || {};
9218 };
9219 /**
9220  * Client Validation Failed
9221  * @const 
9222  */
9223 Roo.form.Action.CLIENT_INVALID = 'client';
9224 /**
9225  * Server Validation Failed
9226  * @const 
9227  */
9228 Roo.form.Action.SERVER_INVALID = 'server';
9229  /**
9230  * Connect to Server Failed
9231  * @const 
9232  */
9233 Roo.form.Action.CONNECT_FAILURE = 'connect';
9234 /**
9235  * Reading Data from Server Failed
9236  * @const 
9237  */
9238 Roo.form.Action.LOAD_FAILURE = 'load';
9239
9240 Roo.form.Action.prototype = {
9241     type : 'default',
9242     failureType : undefined,
9243     response : undefined,
9244     result : undefined,
9245
9246     // interface method
9247     run : function(options){
9248
9249     },
9250
9251     // interface method
9252     success : function(response){
9253
9254     },
9255
9256     // interface method
9257     handleResponse : function(response){
9258
9259     },
9260
9261     // default connection failure
9262     failure : function(response){
9263         
9264         this.response = response;
9265         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9266         this.form.afterAction(this, false);
9267     },
9268
9269     processResponse : function(response){
9270         this.response = response;
9271         if(!response.responseText){
9272             return true;
9273         }
9274         this.result = this.handleResponse(response);
9275         return this.result;
9276     },
9277
9278     // utility functions used internally
9279     getUrl : function(appendParams){
9280         var url = this.options.url || this.form.url || this.form.el.dom.action;
9281         if(appendParams){
9282             var p = this.getParams();
9283             if(p){
9284                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9285             }
9286         }
9287         return url;
9288     },
9289
9290     getMethod : function(){
9291         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9292     },
9293
9294     getParams : function(){
9295         var bp = this.form.baseParams;
9296         var p = this.options.params;
9297         if(p){
9298             if(typeof p == "object"){
9299                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9300             }else if(typeof p == 'string' && bp){
9301                 p += '&' + Roo.urlEncode(bp);
9302             }
9303         }else if(bp){
9304             p = Roo.urlEncode(bp);
9305         }
9306         return p;
9307     },
9308
9309     createCallback : function(){
9310         return {
9311             success: this.success,
9312             failure: this.failure,
9313             scope: this,
9314             timeout: (this.form.timeout*1000),
9315             upload: this.form.fileUpload ? this.success : undefined
9316         };
9317     }
9318 };
9319
9320 Roo.form.Action.Submit = function(form, options){
9321     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9322 };
9323
9324 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9325     type : 'submit',
9326
9327     haveProgress : false,
9328     uploadComplete : false,
9329     
9330     // uploadProgress indicator.
9331     uploadProgress : function()
9332     {
9333         if (!this.form.progressUrl) {
9334             return;
9335         }
9336         
9337         if (!this.haveProgress) {
9338             Roo.MessageBox.progress("Uploading", "Uploading");
9339         }
9340         if (this.uploadComplete) {
9341            Roo.MessageBox.hide();
9342            return;
9343         }
9344         
9345         this.haveProgress = true;
9346    
9347         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9348         
9349         var c = new Roo.data.Connection();
9350         c.request({
9351             url : this.form.progressUrl,
9352             params: {
9353                 id : uid
9354             },
9355             method: 'GET',
9356             success : function(req){
9357                //console.log(data);
9358                 var rdata = false;
9359                 var edata;
9360                 try  {
9361                    rdata = Roo.decode(req.responseText)
9362                 } catch (e) {
9363                     Roo.log("Invalid data from server..");
9364                     Roo.log(edata);
9365                     return;
9366                 }
9367                 if (!rdata || !rdata.success) {
9368                     Roo.log(rdata);
9369                     Roo.MessageBox.alert(Roo.encode(rdata));
9370                     return;
9371                 }
9372                 var data = rdata.data;
9373                 
9374                 if (this.uploadComplete) {
9375                    Roo.MessageBox.hide();
9376                    return;
9377                 }
9378                    
9379                 if (data){
9380                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9381                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9382                     );
9383                 }
9384                 this.uploadProgress.defer(2000,this);
9385             },
9386        
9387             failure: function(data) {
9388                 Roo.log('progress url failed ');
9389                 Roo.log(data);
9390             },
9391             scope : this
9392         });
9393            
9394     },
9395     
9396     
9397     run : function()
9398     {
9399         // run get Values on the form, so it syncs any secondary forms.
9400         this.form.getValues();
9401         
9402         var o = this.options;
9403         var method = this.getMethod();
9404         var isPost = method == 'POST';
9405         if(o.clientValidation === false || this.form.isValid()){
9406             
9407             if (this.form.progressUrl) {
9408                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9409                     (new Date() * 1) + '' + Math.random());
9410                     
9411             } 
9412             
9413             
9414             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9415                 form:this.form.el.dom,
9416                 url:this.getUrl(!isPost),
9417                 method: method,
9418                 params:isPost ? this.getParams() : null,
9419                 isUpload: this.form.fileUpload,
9420                 formData : this.form.formData
9421             }));
9422             
9423             this.uploadProgress();
9424
9425         }else if (o.clientValidation !== false){ // client validation failed
9426             this.failureType = Roo.form.Action.CLIENT_INVALID;
9427             this.form.afterAction(this, false);
9428         }
9429     },
9430
9431     success : function(response)
9432     {
9433         this.uploadComplete= true;
9434         if (this.haveProgress) {
9435             Roo.MessageBox.hide();
9436         }
9437         
9438         
9439         var result = this.processResponse(response);
9440         if(result === true || result.success){
9441             this.form.afterAction(this, true);
9442             return;
9443         }
9444         if(result.errors){
9445             this.form.markInvalid(result.errors);
9446             this.failureType = Roo.form.Action.SERVER_INVALID;
9447         }
9448         this.form.afterAction(this, false);
9449     },
9450     failure : function(response)
9451     {
9452         this.uploadComplete= true;
9453         if (this.haveProgress) {
9454             Roo.MessageBox.hide();
9455         }
9456         
9457         this.response = response;
9458         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9459         this.form.afterAction(this, false);
9460     },
9461     
9462     handleResponse : function(response){
9463         if(this.form.errorReader){
9464             var rs = this.form.errorReader.read(response);
9465             var errors = [];
9466             if(rs.records){
9467                 for(var i = 0, len = rs.records.length; i < len; i++) {
9468                     var r = rs.records[i];
9469                     errors[i] = r.data;
9470                 }
9471             }
9472             if(errors.length < 1){
9473                 errors = null;
9474             }
9475             return {
9476                 success : rs.success,
9477                 errors : errors
9478             };
9479         }
9480         var ret = false;
9481         try {
9482             ret = Roo.decode(response.responseText);
9483         } catch (e) {
9484             ret = {
9485                 success: false,
9486                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9487                 errors : []
9488             };
9489         }
9490         return ret;
9491         
9492     }
9493 });
9494
9495
9496 Roo.form.Action.Load = function(form, options){
9497     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9498     this.reader = this.form.reader;
9499 };
9500
9501 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9502     type : 'load',
9503
9504     run : function(){
9505         
9506         Roo.Ajax.request(Roo.apply(
9507                 this.createCallback(), {
9508                     method:this.getMethod(),
9509                     url:this.getUrl(false),
9510                     params:this.getParams()
9511         }));
9512     },
9513
9514     success : function(response){
9515         
9516         var result = this.processResponse(response);
9517         if(result === true || !result.success || !result.data){
9518             this.failureType = Roo.form.Action.LOAD_FAILURE;
9519             this.form.afterAction(this, false);
9520             return;
9521         }
9522         this.form.clearInvalid();
9523         this.form.setValues(result.data);
9524         this.form.afterAction(this, true);
9525     },
9526
9527     handleResponse : function(response){
9528         if(this.form.reader){
9529             var rs = this.form.reader.read(response);
9530             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9531             return {
9532                 success : rs.success,
9533                 data : data
9534             };
9535         }
9536         return Roo.decode(response.responseText);
9537     }
9538 });
9539
9540 Roo.form.Action.ACTION_TYPES = {
9541     'load' : Roo.form.Action.Load,
9542     'submit' : Roo.form.Action.Submit
9543 };/*
9544  * - LGPL
9545  *
9546  * form
9547  *
9548  */
9549
9550 /**
9551  * @class Roo.bootstrap.Form
9552  * @extends Roo.bootstrap.Component
9553  * Bootstrap Form class
9554  * @cfg {String} method  GET | POST (default POST)
9555  * @cfg {String} labelAlign top | left (default top)
9556  * @cfg {String} align left  | right - for navbars
9557  * @cfg {Boolean} loadMask load mask when submit (default true)
9558
9559  *
9560  * @constructor
9561  * Create a new Form
9562  * @param {Object} config The config object
9563  */
9564
9565
9566 Roo.bootstrap.Form = function(config){
9567     
9568     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9569     
9570     Roo.bootstrap.Form.popover.apply();
9571     
9572     this.addEvents({
9573         /**
9574          * @event clientvalidation
9575          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9576          * @param {Form} this
9577          * @param {Boolean} valid true if the form has passed client-side validation
9578          */
9579         clientvalidation: true,
9580         /**
9581          * @event beforeaction
9582          * Fires before any action is performed. Return false to cancel the action.
9583          * @param {Form} this
9584          * @param {Action} action The action to be performed
9585          */
9586         beforeaction: true,
9587         /**
9588          * @event actionfailed
9589          * Fires when an action fails.
9590          * @param {Form} this
9591          * @param {Action} action The action that failed
9592          */
9593         actionfailed : true,
9594         /**
9595          * @event actioncomplete
9596          * Fires when an action is completed.
9597          * @param {Form} this
9598          * @param {Action} action The action that completed
9599          */
9600         actioncomplete : true
9601     });
9602 };
9603
9604 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9605
9606      /**
9607      * @cfg {String} method
9608      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9609      */
9610     method : 'POST',
9611     /**
9612      * @cfg {String} url
9613      * The URL to use for form actions if one isn't supplied in the action options.
9614      */
9615     /**
9616      * @cfg {Boolean} fileUpload
9617      * Set to true if this form is a file upload.
9618      */
9619
9620     /**
9621      * @cfg {Object} baseParams
9622      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9623      */
9624
9625     /**
9626      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9627      */
9628     timeout: 30,
9629     /**
9630      * @cfg {Sting} align (left|right) for navbar forms
9631      */
9632     align : 'left',
9633
9634     // private
9635     activeAction : null,
9636
9637     /**
9638      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9639      * element by passing it or its id or mask the form itself by passing in true.
9640      * @type Mixed
9641      */
9642     waitMsgTarget : false,
9643
9644     loadMask : true,
9645     
9646     /**
9647      * @cfg {Boolean} errorMask (true|false) default false
9648      */
9649     errorMask : false,
9650     
9651     /**
9652      * @cfg {Number} maskOffset Default 100
9653      */
9654     maskOffset : 100,
9655     
9656     /**
9657      * @cfg {Boolean} maskBody
9658      */
9659     maskBody : false,
9660
9661     getAutoCreate : function(){
9662
9663         var cfg = {
9664             tag: 'form',
9665             method : this.method || 'POST',
9666             id : this.id || Roo.id(),
9667             cls : ''
9668         };
9669         if (this.parent().xtype.match(/^Nav/)) {
9670             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9671
9672         }
9673
9674         if (this.labelAlign == 'left' ) {
9675             cfg.cls += ' form-horizontal';
9676         }
9677
9678
9679         return cfg;
9680     },
9681     initEvents : function()
9682     {
9683         this.el.on('submit', this.onSubmit, this);
9684         // this was added as random key presses on the form where triggering form submit.
9685         this.el.on('keypress', function(e) {
9686             if (e.getCharCode() != 13) {
9687                 return true;
9688             }
9689             // we might need to allow it for textareas.. and some other items.
9690             // check e.getTarget().
9691
9692             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9693                 return true;
9694             }
9695
9696             Roo.log("keypress blocked");
9697
9698             e.preventDefault();
9699             return false;
9700         });
9701         
9702     },
9703     // private
9704     onSubmit : function(e){
9705         e.stopEvent();
9706     },
9707
9708      /**
9709      * Returns true if client-side validation on the form is successful.
9710      * @return Boolean
9711      */
9712     isValid : function(){
9713         var items = this.getItems();
9714         var valid = true;
9715         var target = false;
9716         
9717         items.each(function(f){
9718             
9719             if(f.validate()){
9720                 return;
9721             }
9722             
9723             Roo.log('invalid field: ' + f.name);
9724             
9725             valid = false;
9726
9727             if(!target && f.el.isVisible(true)){
9728                 target = f;
9729             }
9730            
9731         });
9732         
9733         if(this.errorMask && !valid){
9734             Roo.bootstrap.Form.popover.mask(this, target);
9735         }
9736         
9737         return valid;
9738     },
9739     
9740     /**
9741      * Returns true if any fields in this form have changed since their original load.
9742      * @return Boolean
9743      */
9744     isDirty : function(){
9745         var dirty = false;
9746         var items = this.getItems();
9747         items.each(function(f){
9748            if(f.isDirty()){
9749                dirty = true;
9750                return false;
9751            }
9752            return true;
9753         });
9754         return dirty;
9755     },
9756      /**
9757      * Performs a predefined action (submit or load) or custom actions you define on this form.
9758      * @param {String} actionName The name of the action type
9759      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9760      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9761      * accept other config options):
9762      * <pre>
9763 Property          Type             Description
9764 ----------------  ---------------  ----------------------------------------------------------------------------------
9765 url               String           The url for the action (defaults to the form's url)
9766 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9767 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9768 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9769                                    validate the form on the client (defaults to false)
9770      * </pre>
9771      * @return {BasicForm} this
9772      */
9773     doAction : function(action, options){
9774         if(typeof action == 'string'){
9775             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9776         }
9777         if(this.fireEvent('beforeaction', this, action) !== false){
9778             this.beforeAction(action);
9779             action.run.defer(100, action);
9780         }
9781         return this;
9782     },
9783
9784     // private
9785     beforeAction : function(action){
9786         var o = action.options;
9787         
9788         if(this.loadMask){
9789             
9790             if(this.maskBody){
9791                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9792             } else {
9793                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9794             }
9795         }
9796         // not really supported yet.. ??
9797
9798         //if(this.waitMsgTarget === true){
9799         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9800         //}else if(this.waitMsgTarget){
9801         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9802         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9803         //}else {
9804         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9805        // }
9806
9807     },
9808
9809     // private
9810     afterAction : function(action, success){
9811         this.activeAction = null;
9812         var o = action.options;
9813
9814         if(this.loadMask){
9815             
9816             if(this.maskBody){
9817                 Roo.get(document.body).unmask();
9818             } else {
9819                 this.el.unmask();
9820             }
9821         }
9822         
9823         //if(this.waitMsgTarget === true){
9824 //            this.el.unmask();
9825         //}else if(this.waitMsgTarget){
9826         //    this.waitMsgTarget.unmask();
9827         //}else{
9828         //    Roo.MessageBox.updateProgress(1);
9829         //    Roo.MessageBox.hide();
9830        // }
9831         //
9832         if(success){
9833             if(o.reset){
9834                 this.reset();
9835             }
9836             Roo.callback(o.success, o.scope, [this, action]);
9837             this.fireEvent('actioncomplete', this, action);
9838
9839         }else{
9840
9841             // failure condition..
9842             // we have a scenario where updates need confirming.
9843             // eg. if a locking scenario exists..
9844             // we look for { errors : { needs_confirm : true }} in the response.
9845             if (
9846                 (typeof(action.result) != 'undefined')  &&
9847                 (typeof(action.result.errors) != 'undefined')  &&
9848                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9849            ){
9850                 var _t = this;
9851                 Roo.log("not supported yet");
9852                  /*
9853
9854                 Roo.MessageBox.confirm(
9855                     "Change requires confirmation",
9856                     action.result.errorMsg,
9857                     function(r) {
9858                         if (r != 'yes') {
9859                             return;
9860                         }
9861                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9862                     }
9863
9864                 );
9865                 */
9866
9867
9868                 return;
9869             }
9870
9871             Roo.callback(o.failure, o.scope, [this, action]);
9872             // show an error message if no failed handler is set..
9873             if (!this.hasListener('actionfailed')) {
9874                 Roo.log("need to add dialog support");
9875                 /*
9876                 Roo.MessageBox.alert("Error",
9877                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9878                         action.result.errorMsg :
9879                         "Saving Failed, please check your entries or try again"
9880                 );
9881                 */
9882             }
9883
9884             this.fireEvent('actionfailed', this, action);
9885         }
9886
9887     },
9888     /**
9889      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9890      * @param {String} id The value to search for
9891      * @return Field
9892      */
9893     findField : function(id){
9894         var items = this.getItems();
9895         var field = items.get(id);
9896         if(!field){
9897              items.each(function(f){
9898                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9899                     field = f;
9900                     return false;
9901                 }
9902                 return true;
9903             });
9904         }
9905         return field || null;
9906     },
9907      /**
9908      * Mark fields in this form invalid in bulk.
9909      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9910      * @return {BasicForm} this
9911      */
9912     markInvalid : function(errors){
9913         if(errors instanceof Array){
9914             for(var i = 0, len = errors.length; i < len; i++){
9915                 var fieldError = errors[i];
9916                 var f = this.findField(fieldError.id);
9917                 if(f){
9918                     f.markInvalid(fieldError.msg);
9919                 }
9920             }
9921         }else{
9922             var field, id;
9923             for(id in errors){
9924                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9925                     field.markInvalid(errors[id]);
9926                 }
9927             }
9928         }
9929         //Roo.each(this.childForms || [], function (f) {
9930         //    f.markInvalid(errors);
9931         //});
9932
9933         return this;
9934     },
9935
9936     /**
9937      * Set values for fields in this form in bulk.
9938      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9939      * @return {BasicForm} this
9940      */
9941     setValues : function(values){
9942         if(values instanceof Array){ // array of objects
9943             for(var i = 0, len = values.length; i < len; i++){
9944                 var v = values[i];
9945                 var f = this.findField(v.id);
9946                 if(f){
9947                     f.setValue(v.value);
9948                     if(this.trackResetOnLoad){
9949                         f.originalValue = f.getValue();
9950                     }
9951                 }
9952             }
9953         }else{ // object hash
9954             var field, id;
9955             for(id in values){
9956                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9957
9958                     if (field.setFromData &&
9959                         field.valueField &&
9960                         field.displayField &&
9961                         // combos' with local stores can
9962                         // be queried via setValue()
9963                         // to set their value..
9964                         (field.store && !field.store.isLocal)
9965                         ) {
9966                         // it's a combo
9967                         var sd = { };
9968                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9969                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9970                         field.setFromData(sd);
9971
9972                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9973                         
9974                         field.setFromData(values);
9975                         
9976                     } else {
9977                         field.setValue(values[id]);
9978                     }
9979
9980
9981                     if(this.trackResetOnLoad){
9982                         field.originalValue = field.getValue();
9983                     }
9984                 }
9985             }
9986         }
9987
9988         //Roo.each(this.childForms || [], function (f) {
9989         //    f.setValues(values);
9990         //});
9991
9992         return this;
9993     },
9994
9995     /**
9996      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9997      * they are returned as an array.
9998      * @param {Boolean} asString
9999      * @return {Object}
10000      */
10001     getValues : function(asString){
10002         //if (this.childForms) {
10003             // copy values from the child forms
10004         //    Roo.each(this.childForms, function (f) {
10005         //        this.setValues(f.getValues());
10006         //    }, this);
10007         //}
10008
10009
10010
10011         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10012         if(asString === true){
10013             return fs;
10014         }
10015         return Roo.urlDecode(fs);
10016     },
10017
10018     /**
10019      * Returns the fields in this form as an object with key/value pairs.
10020      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10021      * @return {Object}
10022      */
10023     getFieldValues : function(with_hidden)
10024     {
10025         var items = this.getItems();
10026         var ret = {};
10027         items.each(function(f){
10028             
10029             if (!f.getName()) {
10030                 return;
10031             }
10032             
10033             var v = f.getValue();
10034             
10035             if (f.inputType =='radio') {
10036                 if (typeof(ret[f.getName()]) == 'undefined') {
10037                     ret[f.getName()] = ''; // empty..
10038                 }
10039
10040                 if (!f.el.dom.checked) {
10041                     return;
10042
10043                 }
10044                 v = f.el.dom.value;
10045
10046             }
10047             
10048             if(f.xtype == 'MoneyField'){
10049                 ret[f.currencyName] = f.getCurrency();
10050             }
10051
10052             // not sure if this supported any more..
10053             if ((typeof(v) == 'object') && f.getRawValue) {
10054                 v = f.getRawValue() ; // dates..
10055             }
10056             // combo boxes where name != hiddenName...
10057             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10058                 ret[f.name] = f.getRawValue();
10059             }
10060             ret[f.getName()] = v;
10061         });
10062
10063         return ret;
10064     },
10065
10066     /**
10067      * Clears all invalid messages in this form.
10068      * @return {BasicForm} this
10069      */
10070     clearInvalid : function(){
10071         var items = this.getItems();
10072
10073         items.each(function(f){
10074            f.clearInvalid();
10075         });
10076
10077         return this;
10078     },
10079
10080     /**
10081      * Resets this form.
10082      * @return {BasicForm} this
10083      */
10084     reset : function(){
10085         var items = this.getItems();
10086         items.each(function(f){
10087             f.reset();
10088         });
10089
10090         Roo.each(this.childForms || [], function (f) {
10091             f.reset();
10092         });
10093
10094
10095         return this;
10096     },
10097     
10098     getItems : function()
10099     {
10100         var r=new Roo.util.MixedCollection(false, function(o){
10101             return o.id || (o.id = Roo.id());
10102         });
10103         var iter = function(el) {
10104             if (el.inputEl) {
10105                 r.add(el);
10106             }
10107             if (!el.items) {
10108                 return;
10109             }
10110             Roo.each(el.items,function(e) {
10111                 iter(e);
10112             });
10113         };
10114
10115         iter(this);
10116         return r;
10117     },
10118     
10119     hideFields : function(items)
10120     {
10121         Roo.each(items, function(i){
10122             
10123             var f = this.findField(i);
10124             
10125             if(!f){
10126                 return;
10127             }
10128             
10129             f.hide();
10130             
10131         }, this);
10132     },
10133     
10134     showFields : function(items)
10135     {
10136         Roo.each(items, function(i){
10137             
10138             var f = this.findField(i);
10139             
10140             if(!f){
10141                 return;
10142             }
10143             
10144             f.show();
10145             
10146         }, this);
10147     }
10148
10149 });
10150
10151 Roo.apply(Roo.bootstrap.Form, {
10152     
10153     popover : {
10154         
10155         padding : 5,
10156         
10157         isApplied : false,
10158         
10159         isMasked : false,
10160         
10161         form : false,
10162         
10163         target : false,
10164         
10165         toolTip : false,
10166         
10167         intervalID : false,
10168         
10169         maskEl : false,
10170         
10171         apply : function()
10172         {
10173             if(this.isApplied){
10174                 return;
10175             }
10176             
10177             this.maskEl = {
10178                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10179                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10180                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10181                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10182             };
10183             
10184             this.maskEl.top.enableDisplayMode("block");
10185             this.maskEl.left.enableDisplayMode("block");
10186             this.maskEl.bottom.enableDisplayMode("block");
10187             this.maskEl.right.enableDisplayMode("block");
10188             
10189             this.toolTip = new Roo.bootstrap.Tooltip({
10190                 cls : 'roo-form-error-popover',
10191                 alignment : {
10192                     'left' : ['r-l', [-2,0], 'right'],
10193                     'right' : ['l-r', [2,0], 'left'],
10194                     'bottom' : ['tl-bl', [0,2], 'top'],
10195                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10196                 }
10197             });
10198             
10199             this.toolTip.render(Roo.get(document.body));
10200
10201             this.toolTip.el.enableDisplayMode("block");
10202             
10203             Roo.get(document.body).on('click', function(){
10204                 this.unmask();
10205             }, this);
10206             
10207             Roo.get(document.body).on('touchstart', function(){
10208                 this.unmask();
10209             }, this);
10210             
10211             this.isApplied = true
10212         },
10213         
10214         mask : function(form, target)
10215         {
10216             this.form = form;
10217             
10218             this.target = target;
10219             
10220             if(!this.form.errorMask || !target.el){
10221                 return;
10222             }
10223             
10224             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10225             
10226             Roo.log(scrollable);
10227             
10228             var ot = this.target.el.calcOffsetsTo(scrollable);
10229             
10230             var scrollTo = ot[1] - this.form.maskOffset;
10231             
10232             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10233             
10234             scrollable.scrollTo('top', scrollTo);
10235             
10236             var box = this.target.el.getBox();
10237             Roo.log(box);
10238             var zIndex = Roo.bootstrap.Modal.zIndex++;
10239
10240             
10241             this.maskEl.top.setStyle('position', 'absolute');
10242             this.maskEl.top.setStyle('z-index', zIndex);
10243             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10244             this.maskEl.top.setLeft(0);
10245             this.maskEl.top.setTop(0);
10246             this.maskEl.top.show();
10247             
10248             this.maskEl.left.setStyle('position', 'absolute');
10249             this.maskEl.left.setStyle('z-index', zIndex);
10250             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10251             this.maskEl.left.setLeft(0);
10252             this.maskEl.left.setTop(box.y - this.padding);
10253             this.maskEl.left.show();
10254
10255             this.maskEl.bottom.setStyle('position', 'absolute');
10256             this.maskEl.bottom.setStyle('z-index', zIndex);
10257             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10258             this.maskEl.bottom.setLeft(0);
10259             this.maskEl.bottom.setTop(box.bottom + this.padding);
10260             this.maskEl.bottom.show();
10261
10262             this.maskEl.right.setStyle('position', 'absolute');
10263             this.maskEl.right.setStyle('z-index', zIndex);
10264             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10265             this.maskEl.right.setLeft(box.right + this.padding);
10266             this.maskEl.right.setTop(box.y - this.padding);
10267             this.maskEl.right.show();
10268
10269             this.toolTip.bindEl = this.target.el;
10270
10271             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10272
10273             var tip = this.target.blankText;
10274
10275             if(this.target.getValue() !== '' ) {
10276                 
10277                 if (this.target.invalidText.length) {
10278                     tip = this.target.invalidText;
10279                 } else if (this.target.regexText.length){
10280                     tip = this.target.regexText;
10281                 }
10282             }
10283
10284             this.toolTip.show(tip);
10285
10286             this.intervalID = window.setInterval(function() {
10287                 Roo.bootstrap.Form.popover.unmask();
10288             }, 10000);
10289
10290             window.onwheel = function(){ return false;};
10291             
10292             (function(){ this.isMasked = true; }).defer(500, this);
10293             
10294         },
10295         
10296         unmask : function()
10297         {
10298             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10299                 return;
10300             }
10301             
10302             this.maskEl.top.setStyle('position', 'absolute');
10303             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10304             this.maskEl.top.hide();
10305
10306             this.maskEl.left.setStyle('position', 'absolute');
10307             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10308             this.maskEl.left.hide();
10309
10310             this.maskEl.bottom.setStyle('position', 'absolute');
10311             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10312             this.maskEl.bottom.hide();
10313
10314             this.maskEl.right.setStyle('position', 'absolute');
10315             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10316             this.maskEl.right.hide();
10317             
10318             this.toolTip.hide();
10319             
10320             this.toolTip.el.hide();
10321             
10322             window.onwheel = function(){ return true;};
10323             
10324             if(this.intervalID){
10325                 window.clearInterval(this.intervalID);
10326                 this.intervalID = false;
10327             }
10328             
10329             this.isMasked = false;
10330             
10331         }
10332         
10333     }
10334     
10335 });
10336
10337 /*
10338  * Based on:
10339  * Ext JS Library 1.1.1
10340  * Copyright(c) 2006-2007, Ext JS, LLC.
10341  *
10342  * Originally Released Under LGPL - original licence link has changed is not relivant.
10343  *
10344  * Fork - LGPL
10345  * <script type="text/javascript">
10346  */
10347 /**
10348  * @class Roo.form.VTypes
10349  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10350  * @singleton
10351  */
10352 Roo.form.VTypes = function(){
10353     // closure these in so they are only created once.
10354     var alpha = /^[a-zA-Z_]+$/;
10355     var alphanum = /^[a-zA-Z0-9_]+$/;
10356     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10357     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10358
10359     // All these messages and functions are configurable
10360     return {
10361         /**
10362          * The function used to validate email addresses
10363          * @param {String} value The email address
10364          */
10365         'email' : function(v){
10366             return email.test(v);
10367         },
10368         /**
10369          * The error text to display when the email validation function returns false
10370          * @type String
10371          */
10372         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10373         /**
10374          * The keystroke filter mask to be applied on email input
10375          * @type RegExp
10376          */
10377         'emailMask' : /[a-z0-9_\.\-@]/i,
10378
10379         /**
10380          * The function used to validate URLs
10381          * @param {String} value The URL
10382          */
10383         'url' : function(v){
10384             return url.test(v);
10385         },
10386         /**
10387          * The error text to display when the url validation function returns false
10388          * @type String
10389          */
10390         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10391         
10392         /**
10393          * The function used to validate alpha values
10394          * @param {String} value The value
10395          */
10396         'alpha' : function(v){
10397             return alpha.test(v);
10398         },
10399         /**
10400          * The error text to display when the alpha validation function returns false
10401          * @type String
10402          */
10403         'alphaText' : 'This field should only contain letters and _',
10404         /**
10405          * The keystroke filter mask to be applied on alpha input
10406          * @type RegExp
10407          */
10408         'alphaMask' : /[a-z_]/i,
10409
10410         /**
10411          * The function used to validate alphanumeric values
10412          * @param {String} value The value
10413          */
10414         'alphanum' : function(v){
10415             return alphanum.test(v);
10416         },
10417         /**
10418          * The error text to display when the alphanumeric validation function returns false
10419          * @type String
10420          */
10421         'alphanumText' : 'This field should only contain letters, numbers and _',
10422         /**
10423          * The keystroke filter mask to be applied on alphanumeric input
10424          * @type RegExp
10425          */
10426         'alphanumMask' : /[a-z0-9_]/i
10427     };
10428 }();/*
10429  * - LGPL
10430  *
10431  * Input
10432  * 
10433  */
10434
10435 /**
10436  * @class Roo.bootstrap.Input
10437  * @extends Roo.bootstrap.Component
10438  * Bootstrap Input class
10439  * @cfg {Boolean} disabled is it disabled
10440  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10441  * @cfg {String} name name of the input
10442  * @cfg {string} fieldLabel - the label associated
10443  * @cfg {string} placeholder - placeholder to put in text.
10444  * @cfg {string}  before - input group add on before
10445  * @cfg {string} after - input group add on after
10446  * @cfg {string} size - (lg|sm) or leave empty..
10447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10449  * @cfg {Number} md colspan out of 12 for computer-sized screens
10450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10451  * @cfg {string} value default value of the input
10452  * @cfg {Number} labelWidth set the width of label 
10453  * @cfg {Number} labellg set the width of label (1-12)
10454  * @cfg {Number} labelmd set the width of label (1-12)
10455  * @cfg {Number} labelsm set the width of label (1-12)
10456  * @cfg {Number} labelxs set the width of label (1-12)
10457  * @cfg {String} labelAlign (top|left)
10458  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10459  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10460  * @cfg {String} indicatorpos (left|right) default left
10461  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10462  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10463  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10464
10465  * @cfg {String} align (left|center|right) Default left
10466  * @cfg {Boolean} forceFeedback (true|false) Default false
10467  * 
10468  * @constructor
10469  * Create a new Input
10470  * @param {Object} config The config object
10471  */
10472
10473 Roo.bootstrap.Input = function(config){
10474     
10475     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10476     
10477     this.addEvents({
10478         /**
10479          * @event focus
10480          * Fires when this field receives input focus.
10481          * @param {Roo.form.Field} this
10482          */
10483         focus : true,
10484         /**
10485          * @event blur
10486          * Fires when this field loses input focus.
10487          * @param {Roo.form.Field} this
10488          */
10489         blur : true,
10490         /**
10491          * @event specialkey
10492          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10493          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10494          * @param {Roo.form.Field} this
10495          * @param {Roo.EventObject} e The event object
10496          */
10497         specialkey : true,
10498         /**
10499          * @event change
10500          * Fires just before the field blurs if the field value has changed.
10501          * @param {Roo.form.Field} this
10502          * @param {Mixed} newValue The new value
10503          * @param {Mixed} oldValue The original value
10504          */
10505         change : true,
10506         /**
10507          * @event invalid
10508          * Fires after the field has been marked as invalid.
10509          * @param {Roo.form.Field} this
10510          * @param {String} msg The validation message
10511          */
10512         invalid : true,
10513         /**
10514          * @event valid
10515          * Fires after the field has been validated with no errors.
10516          * @param {Roo.form.Field} this
10517          */
10518         valid : true,
10519          /**
10520          * @event keyup
10521          * Fires after the key up
10522          * @param {Roo.form.Field} this
10523          * @param {Roo.EventObject}  e The event Object
10524          */
10525         keyup : true,
10526         /**
10527          * @event paste
10528          * Fires after the user pastes into input
10529          * @param {Roo.form.Field} this
10530          * @param {Roo.EventObject}  e The event Object
10531          */
10532         paste : true
10533     });
10534 };
10535
10536 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10537      /**
10538      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10539       automatic validation (defaults to "keyup").
10540      */
10541     validationEvent : "keyup",
10542      /**
10543      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10544      */
10545     validateOnBlur : true,
10546     /**
10547      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10548      */
10549     validationDelay : 250,
10550      /**
10551      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10552      */
10553     focusClass : "x-form-focus",  // not needed???
10554     
10555        
10556     /**
10557      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10558      */
10559     invalidClass : "has-warning",
10560     
10561     /**
10562      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10563      */
10564     validClass : "has-success",
10565     
10566     /**
10567      * @cfg {Boolean} hasFeedback (true|false) default true
10568      */
10569     hasFeedback : true,
10570     
10571     /**
10572      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10573      */
10574     invalidFeedbackClass : "glyphicon-warning-sign",
10575     
10576     /**
10577      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10578      */
10579     validFeedbackClass : "glyphicon-ok",
10580     
10581     /**
10582      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10583      */
10584     selectOnFocus : false,
10585     
10586      /**
10587      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10588      */
10589     maskRe : null,
10590        /**
10591      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10592      */
10593     vtype : null,
10594     
10595       /**
10596      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10597      */
10598     disableKeyFilter : false,
10599     
10600        /**
10601      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10602      */
10603     disabled : false,
10604      /**
10605      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10606      */
10607     allowBlank : true,
10608     /**
10609      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10610      */
10611     blankText : "Please complete this mandatory field",
10612     
10613      /**
10614      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10615      */
10616     minLength : 0,
10617     /**
10618      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10619      */
10620     maxLength : Number.MAX_VALUE,
10621     /**
10622      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10623      */
10624     minLengthText : "The minimum length for this field is {0}",
10625     /**
10626      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10627      */
10628     maxLengthText : "The maximum length for this field is {0}",
10629   
10630     
10631     /**
10632      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10633      * If available, this function will be called only after the basic validators all return true, and will be passed the
10634      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10635      */
10636     validator : null,
10637     /**
10638      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10639      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10640      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10641      */
10642     regex : null,
10643     /**
10644      * @cfg {String} regexText -- Depricated - use Invalid Text
10645      */
10646     regexText : "",
10647     
10648     /**
10649      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10650      */
10651     invalidText : "",
10652     
10653     
10654     
10655     autocomplete: false,
10656     
10657     
10658     fieldLabel : '',
10659     inputType : 'text',
10660     
10661     name : false,
10662     placeholder: false,
10663     before : false,
10664     after : false,
10665     size : false,
10666     hasFocus : false,
10667     preventMark: false,
10668     isFormField : true,
10669     value : '',
10670     labelWidth : 2,
10671     labelAlign : false,
10672     readOnly : false,
10673     align : false,
10674     formatedValue : false,
10675     forceFeedback : false,
10676     
10677     indicatorpos : 'left',
10678     
10679     labellg : 0,
10680     labelmd : 0,
10681     labelsm : 0,
10682     labelxs : 0,
10683     
10684     capture : '',
10685     accept : '',
10686     
10687     parentLabelAlign : function()
10688     {
10689         var parent = this;
10690         while (parent.parent()) {
10691             parent = parent.parent();
10692             if (typeof(parent.labelAlign) !='undefined') {
10693                 return parent.labelAlign;
10694             }
10695         }
10696         return 'left';
10697         
10698     },
10699     
10700     getAutoCreate : function()
10701     {
10702         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10703         
10704         var id = Roo.id();
10705         
10706         var cfg = {};
10707         
10708         if(this.inputType != 'hidden'){
10709             cfg.cls = 'form-group' //input-group
10710         }
10711         
10712         var input =  {
10713             tag: 'input',
10714             id : id,
10715             type : this.inputType,
10716             value : this.value,
10717             cls : 'form-control',
10718             placeholder : this.placeholder || '',
10719             autocomplete : this.autocomplete || 'new-password'
10720         };
10721         if (this.inputType == 'file') {
10722             input.style = 'overflow:hidden'; // why not in CSS?
10723         }
10724         
10725         if(this.capture.length){
10726             input.capture = this.capture;
10727         }
10728         
10729         if(this.accept.length){
10730             input.accept = this.accept + "/*";
10731         }
10732         
10733         if(this.align){
10734             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10735         }
10736         
10737         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10738             input.maxLength = this.maxLength;
10739         }
10740         
10741         if (this.disabled) {
10742             input.disabled=true;
10743         }
10744         
10745         if (this.readOnly) {
10746             input.readonly=true;
10747         }
10748         
10749         if (this.name) {
10750             input.name = this.name;
10751         }
10752         
10753         if (this.size) {
10754             input.cls += ' input-' + this.size;
10755         }
10756         
10757         var settings=this;
10758         ['xs','sm','md','lg'].map(function(size){
10759             if (settings[size]) {
10760                 cfg.cls += ' col-' + size + '-' + settings[size];
10761             }
10762         });
10763         
10764         var inputblock = input;
10765         
10766         var feedback = {
10767             tag: 'span',
10768             cls: 'glyphicon form-control-feedback'
10769         };
10770             
10771         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10772             
10773             inputblock = {
10774                 cls : 'has-feedback',
10775                 cn :  [
10776                     input,
10777                     feedback
10778                 ] 
10779             };  
10780         }
10781         
10782         if (this.before || this.after) {
10783             
10784             inputblock = {
10785                 cls : 'input-group',
10786                 cn :  [] 
10787             };
10788             
10789             if (this.before && typeof(this.before) == 'string') {
10790                 
10791                 inputblock.cn.push({
10792                     tag :'span',
10793                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10794                     html : this.before
10795                 });
10796             }
10797             if (this.before && typeof(this.before) == 'object') {
10798                 this.before = Roo.factory(this.before);
10799                 
10800                 inputblock.cn.push({
10801                     tag :'span',
10802                     cls : 'roo-input-before input-group-prepend   input-group-' +
10803                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10804                 });
10805             }
10806             
10807             inputblock.cn.push(input);
10808             
10809             if (this.after && typeof(this.after) == 'string') {
10810                 inputblock.cn.push({
10811                     tag :'span',
10812                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10813                     html : this.after
10814                 });
10815             }
10816             if (this.after && typeof(this.after) == 'object') {
10817                 this.after = Roo.factory(this.after);
10818                 
10819                 inputblock.cn.push({
10820                     tag :'span',
10821                     cls : 'roo-input-after input-group-append  input-group-' +
10822                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10823                 });
10824             }
10825             
10826             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10827                 inputblock.cls += ' has-feedback';
10828                 inputblock.cn.push(feedback);
10829             }
10830         };
10831         var indicator = {
10832             tag : 'i',
10833             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10834             tooltip : 'This field is required'
10835         };
10836         if (this.allowBlank ) {
10837             indicator.style = this.allowBlank ? ' display:none' : '';
10838         }
10839         if (align ==='left' && this.fieldLabel.length) {
10840             
10841             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10842             
10843             cfg.cn = [
10844                 indicator,
10845                 {
10846                     tag: 'label',
10847                     'for' :  id,
10848                     cls : 'control-label col-form-label',
10849                     html : this.fieldLabel
10850
10851                 },
10852                 {
10853                     cls : "", 
10854                     cn: [
10855                         inputblock
10856                     ]
10857                 }
10858             ];
10859             
10860             var labelCfg = cfg.cn[1];
10861             var contentCfg = cfg.cn[2];
10862             
10863             if(this.indicatorpos == 'right'){
10864                 cfg.cn = [
10865                     {
10866                         tag: 'label',
10867                         'for' :  id,
10868                         cls : 'control-label col-form-label',
10869                         cn : [
10870                             {
10871                                 tag : 'span',
10872                                 html : this.fieldLabel
10873                             },
10874                             indicator
10875                         ]
10876                     },
10877                     {
10878                         cls : "",
10879                         cn: [
10880                             inputblock
10881                         ]
10882                     }
10883
10884                 ];
10885                 
10886                 labelCfg = cfg.cn[0];
10887                 contentCfg = cfg.cn[1];
10888             
10889             }
10890             
10891             if(this.labelWidth > 12){
10892                 labelCfg.style = "width: " + this.labelWidth + 'px';
10893             }
10894             
10895             if(this.labelWidth < 13 && this.labelmd == 0){
10896                 this.labelmd = this.labelWidth;
10897             }
10898             
10899             if(this.labellg > 0){
10900                 labelCfg.cls += ' col-lg-' + this.labellg;
10901                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10902             }
10903             
10904             if(this.labelmd > 0){
10905                 labelCfg.cls += ' col-md-' + this.labelmd;
10906                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10907             }
10908             
10909             if(this.labelsm > 0){
10910                 labelCfg.cls += ' col-sm-' + this.labelsm;
10911                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10912             }
10913             
10914             if(this.labelxs > 0){
10915                 labelCfg.cls += ' col-xs-' + this.labelxs;
10916                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10917             }
10918             
10919             
10920         } else if ( this.fieldLabel.length) {
10921                 
10922             
10923             
10924             cfg.cn = [
10925                 {
10926                     tag : 'i',
10927                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10928                     tooltip : 'This field is required',
10929                     style : this.allowBlank ? ' display:none' : '' 
10930                 },
10931                 {
10932                     tag: 'label',
10933                    //cls : 'input-group-addon',
10934                     html : this.fieldLabel
10935
10936                 },
10937
10938                inputblock
10939
10940            ];
10941            
10942            if(this.indicatorpos == 'right'){
10943        
10944                 cfg.cn = [
10945                     {
10946                         tag: 'label',
10947                        //cls : 'input-group-addon',
10948                         html : this.fieldLabel
10949
10950                     },
10951                     {
10952                         tag : 'i',
10953                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10954                         tooltip : 'This field is required',
10955                         style : this.allowBlank ? ' display:none' : '' 
10956                     },
10957
10958                    inputblock
10959
10960                ];
10961
10962             }
10963
10964         } else {
10965             
10966             cfg.cn = [
10967
10968                     inputblock
10969
10970             ];
10971                 
10972                 
10973         };
10974         
10975         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10976            cfg.cls += ' navbar-form';
10977         }
10978         
10979         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10980             // on BS4 we do this only if not form 
10981             cfg.cls += ' navbar-form';
10982             cfg.tag = 'li';
10983         }
10984         
10985         return cfg;
10986         
10987     },
10988     /**
10989      * return the real input element.
10990      */
10991     inputEl: function ()
10992     {
10993         return this.el.select('input.form-control',true).first();
10994     },
10995     
10996     tooltipEl : function()
10997     {
10998         return this.inputEl();
10999     },
11000     
11001     indicatorEl : function()
11002     {
11003         if (Roo.bootstrap.version == 4) {
11004             return false; // not enabled in v4 yet.
11005         }
11006         
11007         var indicator = this.el.select('i.roo-required-indicator',true).first();
11008         
11009         if(!indicator){
11010             return false;
11011         }
11012         
11013         return indicator;
11014         
11015     },
11016     
11017     setDisabled : function(v)
11018     {
11019         var i  = this.inputEl().dom;
11020         if (!v) {
11021             i.removeAttribute('disabled');
11022             return;
11023             
11024         }
11025         i.setAttribute('disabled','true');
11026     },
11027     initEvents : function()
11028     {
11029           
11030         this.inputEl().on("keydown" , this.fireKey,  this);
11031         this.inputEl().on("focus", this.onFocus,  this);
11032         this.inputEl().on("blur", this.onBlur,  this);
11033         
11034         this.inputEl().relayEvent('keyup', this);
11035         this.inputEl().relayEvent('paste', this);
11036         
11037         this.indicator = this.indicatorEl();
11038         
11039         if(this.indicator){
11040             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11041         }
11042  
11043         // reference to original value for reset
11044         this.originalValue = this.getValue();
11045         //Roo.form.TextField.superclass.initEvents.call(this);
11046         if(this.validationEvent == 'keyup'){
11047             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11048             this.inputEl().on('keyup', this.filterValidation, this);
11049         }
11050         else if(this.validationEvent !== false){
11051             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11052         }
11053         
11054         if(this.selectOnFocus){
11055             this.on("focus", this.preFocus, this);
11056             
11057         }
11058         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11059             this.inputEl().on("keypress", this.filterKeys, this);
11060         } else {
11061             this.inputEl().relayEvent('keypress', this);
11062         }
11063        /* if(this.grow){
11064             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11065             this.el.on("click", this.autoSize,  this);
11066         }
11067         */
11068         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11069             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11070         }
11071         
11072         if (typeof(this.before) == 'object') {
11073             this.before.render(this.el.select('.roo-input-before',true).first());
11074         }
11075         if (typeof(this.after) == 'object') {
11076             this.after.render(this.el.select('.roo-input-after',true).first());
11077         }
11078         
11079         this.inputEl().on('change', this.onChange, this);
11080         
11081     },
11082     filterValidation : function(e){
11083         if(!e.isNavKeyPress()){
11084             this.validationTask.delay(this.validationDelay);
11085         }
11086     },
11087      /**
11088      * Validates the field value
11089      * @return {Boolean} True if the value is valid, else false
11090      */
11091     validate : function(){
11092         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11093         if(this.disabled || this.validateValue(this.getRawValue())){
11094             this.markValid();
11095             return true;
11096         }
11097         
11098         this.markInvalid();
11099         return false;
11100     },
11101     
11102     
11103     /**
11104      * Validates a value according to the field's validation rules and marks the field as invalid
11105      * if the validation fails
11106      * @param {Mixed} value The value to validate
11107      * @return {Boolean} True if the value is valid, else false
11108      */
11109     validateValue : function(value)
11110     {
11111         if(this.getVisibilityEl().hasClass('hidden')){
11112             return true;
11113         }
11114         
11115         if(value.length < 1)  { // if it's blank
11116             if(this.allowBlank){
11117                 return true;
11118             }
11119             return false;
11120         }
11121         
11122         if(value.length < this.minLength){
11123             return false;
11124         }
11125         if(value.length > this.maxLength){
11126             return false;
11127         }
11128         if(this.vtype){
11129             var vt = Roo.form.VTypes;
11130             if(!vt[this.vtype](value, this)){
11131                 return false;
11132             }
11133         }
11134         if(typeof this.validator == "function"){
11135             var msg = this.validator(value);
11136             if(msg !== true){
11137                 return false;
11138             }
11139             if (typeof(msg) == 'string') {
11140                 this.invalidText = msg;
11141             }
11142         }
11143         
11144         if(this.regex && !this.regex.test(value)){
11145             return false;
11146         }
11147         
11148         return true;
11149     },
11150     
11151      // private
11152     fireKey : function(e){
11153         //Roo.log('field ' + e.getKey());
11154         if(e.isNavKeyPress()){
11155             this.fireEvent("specialkey", this, e);
11156         }
11157     },
11158     focus : function (selectText){
11159         if(this.rendered){
11160             this.inputEl().focus();
11161             if(selectText === true){
11162                 this.inputEl().dom.select();
11163             }
11164         }
11165         return this;
11166     } ,
11167     
11168     onFocus : function(){
11169         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11170            // this.el.addClass(this.focusClass);
11171         }
11172         if(!this.hasFocus){
11173             this.hasFocus = true;
11174             this.startValue = this.getValue();
11175             this.fireEvent("focus", this);
11176         }
11177     },
11178     
11179     beforeBlur : Roo.emptyFn,
11180
11181     
11182     // private
11183     onBlur : function(){
11184         this.beforeBlur();
11185         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11186             //this.el.removeClass(this.focusClass);
11187         }
11188         this.hasFocus = false;
11189         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11190             this.validate();
11191         }
11192         var v = this.getValue();
11193         if(String(v) !== String(this.startValue)){
11194             this.fireEvent('change', this, v, this.startValue);
11195         }
11196         this.fireEvent("blur", this);
11197     },
11198     
11199     onChange : function(e)
11200     {
11201         var v = this.getValue();
11202         if(String(v) !== String(this.startValue)){
11203             this.fireEvent('change', this, v, this.startValue);
11204         }
11205         
11206     },
11207     
11208     /**
11209      * Resets the current field value to the originally loaded value and clears any validation messages
11210      */
11211     reset : function(){
11212         this.setValue(this.originalValue);
11213         this.validate();
11214     },
11215      /**
11216      * Returns the name of the field
11217      * @return {Mixed} name The name field
11218      */
11219     getName: function(){
11220         return this.name;
11221     },
11222      /**
11223      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11224      * @return {Mixed} value The field value
11225      */
11226     getValue : function(){
11227         
11228         var v = this.inputEl().getValue();
11229         
11230         return v;
11231     },
11232     /**
11233      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11234      * @return {Mixed} value The field value
11235      */
11236     getRawValue : function(){
11237         var v = this.inputEl().getValue();
11238         
11239         return v;
11240     },
11241     
11242     /**
11243      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11244      * @param {Mixed} value The value to set
11245      */
11246     setRawValue : function(v){
11247         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11248     },
11249     
11250     selectText : function(start, end){
11251         var v = this.getRawValue();
11252         if(v.length > 0){
11253             start = start === undefined ? 0 : start;
11254             end = end === undefined ? v.length : end;
11255             var d = this.inputEl().dom;
11256             if(d.setSelectionRange){
11257                 d.setSelectionRange(start, end);
11258             }else if(d.createTextRange){
11259                 var range = d.createTextRange();
11260                 range.moveStart("character", start);
11261                 range.moveEnd("character", v.length-end);
11262                 range.select();
11263             }
11264         }
11265     },
11266     
11267     /**
11268      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11269      * @param {Mixed} value The value to set
11270      */
11271     setValue : function(v){
11272         this.value = v;
11273         if(this.rendered){
11274             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11275             this.validate();
11276         }
11277     },
11278     
11279     /*
11280     processValue : function(value){
11281         if(this.stripCharsRe){
11282             var newValue = value.replace(this.stripCharsRe, '');
11283             if(newValue !== value){
11284                 this.setRawValue(newValue);
11285                 return newValue;
11286             }
11287         }
11288         return value;
11289     },
11290   */
11291     preFocus : function(){
11292         
11293         if(this.selectOnFocus){
11294             this.inputEl().dom.select();
11295         }
11296     },
11297     filterKeys : function(e){
11298         var k = e.getKey();
11299         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11300             return;
11301         }
11302         var c = e.getCharCode(), cc = String.fromCharCode(c);
11303         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11304             return;
11305         }
11306         if(!this.maskRe.test(cc)){
11307             e.stopEvent();
11308         }
11309     },
11310      /**
11311      * Clear any invalid styles/messages for this field
11312      */
11313     clearInvalid : function(){
11314         
11315         if(!this.el || this.preventMark){ // not rendered
11316             return;
11317         }
11318         
11319         
11320         this.el.removeClass([this.invalidClass, 'is-invalid']);
11321         
11322         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11323             
11324             var feedback = this.el.select('.form-control-feedback', true).first();
11325             
11326             if(feedback){
11327                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11328             }
11329             
11330         }
11331         
11332         if(this.indicator){
11333             this.indicator.removeClass('visible');
11334             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11335         }
11336         
11337         this.fireEvent('valid', this);
11338     },
11339     
11340      /**
11341      * Mark this field as valid
11342      */
11343     markValid : function()
11344     {
11345         if(!this.el  || this.preventMark){ // not rendered...
11346             return;
11347         }
11348         
11349         this.el.removeClass([this.invalidClass, this.validClass]);
11350         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11351
11352         var feedback = this.el.select('.form-control-feedback', true).first();
11353             
11354         if(feedback){
11355             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11356         }
11357         
11358         if(this.indicator){
11359             this.indicator.removeClass('visible');
11360             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11361         }
11362         
11363         if(this.disabled){
11364             return;
11365         }
11366         
11367            
11368         if(this.allowBlank && !this.getRawValue().length){
11369             return;
11370         }
11371         if (Roo.bootstrap.version == 3) {
11372             this.el.addClass(this.validClass);
11373         } else {
11374             this.inputEl().addClass('is-valid');
11375         }
11376
11377         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11378             
11379             var feedback = this.el.select('.form-control-feedback', true).first();
11380             
11381             if(feedback){
11382                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11383                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11384             }
11385             
11386         }
11387         
11388         this.fireEvent('valid', this);
11389     },
11390     
11391      /**
11392      * Mark this field as invalid
11393      * @param {String} msg The validation message
11394      */
11395     markInvalid : function(msg)
11396     {
11397         if(!this.el  || this.preventMark){ // not rendered
11398             return;
11399         }
11400         
11401         this.el.removeClass([this.invalidClass, this.validClass]);
11402         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11403         
11404         var feedback = this.el.select('.form-control-feedback', true).first();
11405             
11406         if(feedback){
11407             this.el.select('.form-control-feedback', true).first().removeClass(
11408                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11409         }
11410
11411         if(this.disabled){
11412             return;
11413         }
11414         
11415         if(this.allowBlank && !this.getRawValue().length){
11416             return;
11417         }
11418         
11419         if(this.indicator){
11420             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11421             this.indicator.addClass('visible');
11422         }
11423         if (Roo.bootstrap.version == 3) {
11424             this.el.addClass(this.invalidClass);
11425         } else {
11426             this.inputEl().addClass('is-invalid');
11427         }
11428         
11429         
11430         
11431         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11432             
11433             var feedback = this.el.select('.form-control-feedback', true).first();
11434             
11435             if(feedback){
11436                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11437                 
11438                 if(this.getValue().length || this.forceFeedback){
11439                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11440                 }
11441                 
11442             }
11443             
11444         }
11445         
11446         this.fireEvent('invalid', this, msg);
11447     },
11448     // private
11449     SafariOnKeyDown : function(event)
11450     {
11451         // this is a workaround for a password hang bug on chrome/ webkit.
11452         if (this.inputEl().dom.type != 'password') {
11453             return;
11454         }
11455         
11456         var isSelectAll = false;
11457         
11458         if(this.inputEl().dom.selectionEnd > 0){
11459             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11460         }
11461         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11462             event.preventDefault();
11463             this.setValue('');
11464             return;
11465         }
11466         
11467         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11468             
11469             event.preventDefault();
11470             // this is very hacky as keydown always get's upper case.
11471             //
11472             var cc = String.fromCharCode(event.getCharCode());
11473             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11474             
11475         }
11476     },
11477     adjustWidth : function(tag, w){
11478         tag = tag.toLowerCase();
11479         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11480             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11481                 if(tag == 'input'){
11482                     return w + 2;
11483                 }
11484                 if(tag == 'textarea'){
11485                     return w-2;
11486                 }
11487             }else if(Roo.isOpera){
11488                 if(tag == 'input'){
11489                     return w + 2;
11490                 }
11491                 if(tag == 'textarea'){
11492                     return w-2;
11493                 }
11494             }
11495         }
11496         return w;
11497     },
11498     
11499     setFieldLabel : function(v)
11500     {
11501         if(!this.rendered){
11502             return;
11503         }
11504         
11505         if(this.indicatorEl()){
11506             var ar = this.el.select('label > span',true);
11507             
11508             if (ar.elements.length) {
11509                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11510                 this.fieldLabel = v;
11511                 return;
11512             }
11513             
11514             var br = this.el.select('label',true);
11515             
11516             if(br.elements.length) {
11517                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11518                 this.fieldLabel = v;
11519                 return;
11520             }
11521             
11522             Roo.log('Cannot Found any of label > span || label in input');
11523             return;
11524         }
11525         
11526         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11527         this.fieldLabel = v;
11528         
11529         
11530     }
11531 });
11532
11533  
11534 /*
11535  * - LGPL
11536  *
11537  * Input
11538  * 
11539  */
11540
11541 /**
11542  * @class Roo.bootstrap.TextArea
11543  * @extends Roo.bootstrap.Input
11544  * Bootstrap TextArea class
11545  * @cfg {Number} cols Specifies the visible width of a text area
11546  * @cfg {Number} rows Specifies the visible number of lines in a text area
11547  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11548  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11549  * @cfg {string} html text
11550  * 
11551  * @constructor
11552  * Create a new TextArea
11553  * @param {Object} config The config object
11554  */
11555
11556 Roo.bootstrap.TextArea = function(config){
11557     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11558    
11559 };
11560
11561 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11562      
11563     cols : false,
11564     rows : 5,
11565     readOnly : false,
11566     warp : 'soft',
11567     resize : false,
11568     value: false,
11569     html: false,
11570     
11571     getAutoCreate : function(){
11572         
11573         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11574         
11575         var id = Roo.id();
11576         
11577         var cfg = {};
11578         
11579         if(this.inputType != 'hidden'){
11580             cfg.cls = 'form-group' //input-group
11581         }
11582         
11583         var input =  {
11584             tag: 'textarea',
11585             id : id,
11586             warp : this.warp,
11587             rows : this.rows,
11588             value : this.value || '',
11589             html: this.html || '',
11590             cls : 'form-control',
11591             placeholder : this.placeholder || '' 
11592             
11593         };
11594         
11595         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11596             input.maxLength = this.maxLength;
11597         }
11598         
11599         if(this.resize){
11600             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11601         }
11602         
11603         if(this.cols){
11604             input.cols = this.cols;
11605         }
11606         
11607         if (this.readOnly) {
11608             input.readonly = true;
11609         }
11610         
11611         if (this.name) {
11612             input.name = this.name;
11613         }
11614         
11615         if (this.size) {
11616             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11617         }
11618         
11619         var settings=this;
11620         ['xs','sm','md','lg'].map(function(size){
11621             if (settings[size]) {
11622                 cfg.cls += ' col-' + size + '-' + settings[size];
11623             }
11624         });
11625         
11626         var inputblock = input;
11627         
11628         if(this.hasFeedback && !this.allowBlank){
11629             
11630             var feedback = {
11631                 tag: 'span',
11632                 cls: 'glyphicon form-control-feedback'
11633             };
11634
11635             inputblock = {
11636                 cls : 'has-feedback',
11637                 cn :  [
11638                     input,
11639                     feedback
11640                 ] 
11641             };  
11642         }
11643         
11644         
11645         if (this.before || this.after) {
11646             
11647             inputblock = {
11648                 cls : 'input-group',
11649                 cn :  [] 
11650             };
11651             if (this.before) {
11652                 inputblock.cn.push({
11653                     tag :'span',
11654                     cls : 'input-group-addon',
11655                     html : this.before
11656                 });
11657             }
11658             
11659             inputblock.cn.push(input);
11660             
11661             if(this.hasFeedback && !this.allowBlank){
11662                 inputblock.cls += ' has-feedback';
11663                 inputblock.cn.push(feedback);
11664             }
11665             
11666             if (this.after) {
11667                 inputblock.cn.push({
11668                     tag :'span',
11669                     cls : 'input-group-addon',
11670                     html : this.after
11671                 });
11672             }
11673             
11674         }
11675         
11676         if (align ==='left' && this.fieldLabel.length) {
11677             cfg.cn = [
11678                 {
11679                     tag: 'label',
11680                     'for' :  id,
11681                     cls : 'control-label',
11682                     html : this.fieldLabel
11683                 },
11684                 {
11685                     cls : "",
11686                     cn: [
11687                         inputblock
11688                     ]
11689                 }
11690
11691             ];
11692             
11693             if(this.labelWidth > 12){
11694                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11695             }
11696
11697             if(this.labelWidth < 13 && this.labelmd == 0){
11698                 this.labelmd = this.labelWidth;
11699             }
11700
11701             if(this.labellg > 0){
11702                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11703                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11704             }
11705
11706             if(this.labelmd > 0){
11707                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11708                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11709             }
11710
11711             if(this.labelsm > 0){
11712                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11713                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11714             }
11715
11716             if(this.labelxs > 0){
11717                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11718                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11719             }
11720             
11721         } else if ( this.fieldLabel.length) {
11722             cfg.cn = [
11723
11724                {
11725                    tag: 'label',
11726                    //cls : 'input-group-addon',
11727                    html : this.fieldLabel
11728
11729                },
11730
11731                inputblock
11732
11733            ];
11734
11735         } else {
11736
11737             cfg.cn = [
11738
11739                 inputblock
11740
11741             ];
11742                 
11743         }
11744         
11745         if (this.disabled) {
11746             input.disabled=true;
11747         }
11748         
11749         return cfg;
11750         
11751     },
11752     /**
11753      * return the real textarea element.
11754      */
11755     inputEl: function ()
11756     {
11757         return this.el.select('textarea.form-control',true).first();
11758     },
11759     
11760     /**
11761      * Clear any invalid styles/messages for this field
11762      */
11763     clearInvalid : function()
11764     {
11765         
11766         if(!this.el || this.preventMark){ // not rendered
11767             return;
11768         }
11769         
11770         var label = this.el.select('label', true).first();
11771         var icon = this.el.select('i.fa-star', true).first();
11772         
11773         if(label && icon){
11774             icon.remove();
11775         }
11776         this.el.removeClass( this.validClass);
11777         this.inputEl().removeClass('is-invalid');
11778          
11779         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11780             
11781             var feedback = this.el.select('.form-control-feedback', true).first();
11782             
11783             if(feedback){
11784                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11785             }
11786             
11787         }
11788         
11789         this.fireEvent('valid', this);
11790     },
11791     
11792      /**
11793      * Mark this field as valid
11794      */
11795     markValid : function()
11796     {
11797         if(!this.el  || this.preventMark){ // not rendered
11798             return;
11799         }
11800         
11801         this.el.removeClass([this.invalidClass, this.validClass]);
11802         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11803         
11804         var feedback = this.el.select('.form-control-feedback', true).first();
11805             
11806         if(feedback){
11807             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11808         }
11809
11810         if(this.disabled || this.allowBlank){
11811             return;
11812         }
11813         
11814         var label = this.el.select('label', true).first();
11815         var icon = this.el.select('i.fa-star', true).first();
11816         
11817         if(label && icon){
11818             icon.remove();
11819         }
11820         if (Roo.bootstrap.version == 3) {
11821             this.el.addClass(this.validClass);
11822         } else {
11823             this.inputEl().addClass('is-valid');
11824         }
11825         
11826         
11827         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11828             
11829             var feedback = this.el.select('.form-control-feedback', true).first();
11830             
11831             if(feedback){
11832                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11833                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11834             }
11835             
11836         }
11837         
11838         this.fireEvent('valid', this);
11839     },
11840     
11841      /**
11842      * Mark this field as invalid
11843      * @param {String} msg The validation message
11844      */
11845     markInvalid : function(msg)
11846     {
11847         if(!this.el  || this.preventMark){ // not rendered
11848             return;
11849         }
11850         
11851         this.el.removeClass([this.invalidClass, this.validClass]);
11852         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11853         
11854         var feedback = this.el.select('.form-control-feedback', true).first();
11855             
11856         if(feedback){
11857             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11858         }
11859
11860         if(this.disabled || this.allowBlank){
11861             return;
11862         }
11863         
11864         var label = this.el.select('label', true).first();
11865         var icon = this.el.select('i.fa-star', true).first();
11866         
11867         if(!this.getValue().length && label && !icon){
11868             this.el.createChild({
11869                 tag : 'i',
11870                 cls : 'text-danger fa fa-lg fa-star',
11871                 tooltip : 'This field is required',
11872                 style : 'margin-right:5px;'
11873             }, label, true);
11874         }
11875         
11876         if (Roo.bootstrap.version == 3) {
11877             this.el.addClass(this.invalidClass);
11878         } else {
11879             this.inputEl().addClass('is-invalid');
11880         }
11881         
11882         // fixme ... this may be depricated need to test..
11883         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11884             
11885             var feedback = this.el.select('.form-control-feedback', true).first();
11886             
11887             if(feedback){
11888                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11889                 
11890                 if(this.getValue().length || this.forceFeedback){
11891                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11892                 }
11893                 
11894             }
11895             
11896         }
11897         
11898         this.fireEvent('invalid', this, msg);
11899     }
11900 });
11901
11902  
11903 /*
11904  * - LGPL
11905  *
11906  * trigger field - base class for combo..
11907  * 
11908  */
11909  
11910 /**
11911  * @class Roo.bootstrap.TriggerField
11912  * @extends Roo.bootstrap.Input
11913  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11914  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11915  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11916  * for which you can provide a custom implementation.  For example:
11917  * <pre><code>
11918 var trigger = new Roo.bootstrap.TriggerField();
11919 trigger.onTriggerClick = myTriggerFn;
11920 trigger.applyTo('my-field');
11921 </code></pre>
11922  *
11923  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11924  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11925  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11926  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11927  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11928
11929  * @constructor
11930  * Create a new TriggerField.
11931  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11932  * to the base TextField)
11933  */
11934 Roo.bootstrap.TriggerField = function(config){
11935     this.mimicing = false;
11936     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11937 };
11938
11939 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11940     /**
11941      * @cfg {String} triggerClass A CSS class to apply to the trigger
11942      */
11943      /**
11944      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11945      */
11946     hideTrigger:false,
11947
11948     /**
11949      * @cfg {Boolean} removable (true|false) special filter default false
11950      */
11951     removable : false,
11952     
11953     /** @cfg {Boolean} grow @hide */
11954     /** @cfg {Number} growMin @hide */
11955     /** @cfg {Number} growMax @hide */
11956
11957     /**
11958      * @hide 
11959      * @method
11960      */
11961     autoSize: Roo.emptyFn,
11962     // private
11963     monitorTab : true,
11964     // private
11965     deferHeight : true,
11966
11967     
11968     actionMode : 'wrap',
11969     
11970     caret : false,
11971     
11972     
11973     getAutoCreate : function(){
11974        
11975         var align = this.labelAlign || this.parentLabelAlign();
11976         
11977         var id = Roo.id();
11978         
11979         var cfg = {
11980             cls: 'form-group' //input-group
11981         };
11982         
11983         
11984         var input =  {
11985             tag: 'input',
11986             id : id,
11987             type : this.inputType,
11988             cls : 'form-control',
11989             autocomplete: 'new-password',
11990             placeholder : this.placeholder || '' 
11991             
11992         };
11993         if (this.name) {
11994             input.name = this.name;
11995         }
11996         if (this.size) {
11997             input.cls += ' input-' + this.size;
11998         }
11999         
12000         if (this.disabled) {
12001             input.disabled=true;
12002         }
12003         
12004         var inputblock = input;
12005         
12006         if(this.hasFeedback && !this.allowBlank){
12007             
12008             var feedback = {
12009                 tag: 'span',
12010                 cls: 'glyphicon form-control-feedback'
12011             };
12012             
12013             if(this.removable && !this.editable  ){
12014                 inputblock = {
12015                     cls : 'has-feedback',
12016                     cn :  [
12017                         inputblock,
12018                         {
12019                             tag: 'button',
12020                             html : 'x',
12021                             cls : 'roo-combo-removable-btn close'
12022                         },
12023                         feedback
12024                     ] 
12025                 };
12026             } else {
12027                 inputblock = {
12028                     cls : 'has-feedback',
12029                     cn :  [
12030                         inputblock,
12031                         feedback
12032                     ] 
12033                 };
12034             }
12035
12036         } else {
12037             if(this.removable && !this.editable ){
12038                 inputblock = {
12039                     cls : 'roo-removable',
12040                     cn :  [
12041                         inputblock,
12042                         {
12043                             tag: 'button',
12044                             html : 'x',
12045                             cls : 'roo-combo-removable-btn close'
12046                         }
12047                     ] 
12048                 };
12049             }
12050         }
12051         
12052         if (this.before || this.after) {
12053             
12054             inputblock = {
12055                 cls : 'input-group',
12056                 cn :  [] 
12057             };
12058             if (this.before) {
12059                 inputblock.cn.push({
12060                     tag :'span',
12061                     cls : 'input-group-addon input-group-prepend input-group-text',
12062                     html : this.before
12063                 });
12064             }
12065             
12066             inputblock.cn.push(input);
12067             
12068             if(this.hasFeedback && !this.allowBlank){
12069                 inputblock.cls += ' has-feedback';
12070                 inputblock.cn.push(feedback);
12071             }
12072             
12073             if (this.after) {
12074                 inputblock.cn.push({
12075                     tag :'span',
12076                     cls : 'input-group-addon input-group-append input-group-text',
12077                     html : this.after
12078                 });
12079             }
12080             
12081         };
12082         
12083       
12084         
12085         var ibwrap = inputblock;
12086         
12087         if(this.multiple){
12088             ibwrap = {
12089                 tag: 'ul',
12090                 cls: 'roo-select2-choices',
12091                 cn:[
12092                     {
12093                         tag: 'li',
12094                         cls: 'roo-select2-search-field',
12095                         cn: [
12096
12097                             inputblock
12098                         ]
12099                     }
12100                 ]
12101             };
12102                 
12103         }
12104         
12105         var combobox = {
12106             cls: 'roo-select2-container input-group',
12107             cn: [
12108                  {
12109                     tag: 'input',
12110                     type : 'hidden',
12111                     cls: 'form-hidden-field'
12112                 },
12113                 ibwrap
12114             ]
12115         };
12116         
12117         if(!this.multiple && this.showToggleBtn){
12118             
12119             var caret = {
12120                         tag: 'span',
12121                         cls: 'caret'
12122              };
12123             if (this.caret != false) {
12124                 caret = {
12125                      tag: 'i',
12126                      cls: 'fa fa-' + this.caret
12127                 };
12128                 
12129             }
12130             
12131             combobox.cn.push({
12132                 tag :'span',
12133                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12134                 cn : [
12135                     Roo.bootstrap.version == 3 ? caret : '',
12136                     {
12137                         tag: 'span',
12138                         cls: 'combobox-clear',
12139                         cn  : [
12140                             {
12141                                 tag : 'i',
12142                                 cls: 'icon-remove'
12143                             }
12144                         ]
12145                     }
12146                 ]
12147
12148             })
12149         }
12150         
12151         if(this.multiple){
12152             combobox.cls += ' roo-select2-container-multi';
12153         }
12154          var indicator = {
12155             tag : 'i',
12156             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12157             tooltip : 'This field is required'
12158         };
12159         if (Roo.bootstrap.version == 4) {
12160             indicator = {
12161                 tag : 'i',
12162                 style : 'display:none'
12163             };
12164         }
12165         
12166         
12167         if (align ==='left' && this.fieldLabel.length) {
12168             
12169             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12170
12171             cfg.cn = [
12172                 indicator,
12173                 {
12174                     tag: 'label',
12175                     'for' :  id,
12176                     cls : 'control-label',
12177                     html : this.fieldLabel
12178
12179                 },
12180                 {
12181                     cls : "", 
12182                     cn: [
12183                         combobox
12184                     ]
12185                 }
12186
12187             ];
12188             
12189             var labelCfg = cfg.cn[1];
12190             var contentCfg = cfg.cn[2];
12191             
12192             if(this.indicatorpos == 'right'){
12193                 cfg.cn = [
12194                     {
12195                         tag: 'label',
12196                         'for' :  id,
12197                         cls : 'control-label',
12198                         cn : [
12199                             {
12200                                 tag : 'span',
12201                                 html : this.fieldLabel
12202                             },
12203                             indicator
12204                         ]
12205                     },
12206                     {
12207                         cls : "", 
12208                         cn: [
12209                             combobox
12210                         ]
12211                     }
12212
12213                 ];
12214                 
12215                 labelCfg = cfg.cn[0];
12216                 contentCfg = cfg.cn[1];
12217             }
12218             
12219             if(this.labelWidth > 12){
12220                 labelCfg.style = "width: " + this.labelWidth + 'px';
12221             }
12222             
12223             if(this.labelWidth < 13 && this.labelmd == 0){
12224                 this.labelmd = this.labelWidth;
12225             }
12226             
12227             if(this.labellg > 0){
12228                 labelCfg.cls += ' col-lg-' + this.labellg;
12229                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12230             }
12231             
12232             if(this.labelmd > 0){
12233                 labelCfg.cls += ' col-md-' + this.labelmd;
12234                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12235             }
12236             
12237             if(this.labelsm > 0){
12238                 labelCfg.cls += ' col-sm-' + this.labelsm;
12239                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12240             }
12241             
12242             if(this.labelxs > 0){
12243                 labelCfg.cls += ' col-xs-' + this.labelxs;
12244                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12245             }
12246             
12247         } else if ( this.fieldLabel.length) {
12248 //                Roo.log(" label");
12249             cfg.cn = [
12250                 indicator,
12251                {
12252                    tag: 'label',
12253                    //cls : 'input-group-addon',
12254                    html : this.fieldLabel
12255
12256                },
12257
12258                combobox
12259
12260             ];
12261             
12262             if(this.indicatorpos == 'right'){
12263                 
12264                 cfg.cn = [
12265                     {
12266                        tag: 'label',
12267                        cn : [
12268                            {
12269                                tag : 'span',
12270                                html : this.fieldLabel
12271                            },
12272                            indicator
12273                        ]
12274
12275                     },
12276                     combobox
12277
12278                 ];
12279
12280             }
12281
12282         } else {
12283             
12284 //                Roo.log(" no label && no align");
12285                 cfg = combobox
12286                      
12287                 
12288         }
12289         
12290         var settings=this;
12291         ['xs','sm','md','lg'].map(function(size){
12292             if (settings[size]) {
12293                 cfg.cls += ' col-' + size + '-' + settings[size];
12294             }
12295         });
12296         
12297         return cfg;
12298         
12299     },
12300     
12301     
12302     
12303     // private
12304     onResize : function(w, h){
12305 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12306 //        if(typeof w == 'number'){
12307 //            var x = w - this.trigger.getWidth();
12308 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12309 //            this.trigger.setStyle('left', x+'px');
12310 //        }
12311     },
12312
12313     // private
12314     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12315
12316     // private
12317     getResizeEl : function(){
12318         return this.inputEl();
12319     },
12320
12321     // private
12322     getPositionEl : function(){
12323         return this.inputEl();
12324     },
12325
12326     // private
12327     alignErrorIcon : function(){
12328         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12329     },
12330
12331     // private
12332     initEvents : function(){
12333         
12334         this.createList();
12335         
12336         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12337         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12338         if(!this.multiple && this.showToggleBtn){
12339             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12340             if(this.hideTrigger){
12341                 this.trigger.setDisplayed(false);
12342             }
12343             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12344         }
12345         
12346         if(this.multiple){
12347             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12348         }
12349         
12350         if(this.removable && !this.editable && !this.tickable){
12351             var close = this.closeTriggerEl();
12352             
12353             if(close){
12354                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12355                 close.on('click', this.removeBtnClick, this, close);
12356             }
12357         }
12358         
12359         //this.trigger.addClassOnOver('x-form-trigger-over');
12360         //this.trigger.addClassOnClick('x-form-trigger-click');
12361         
12362         //if(!this.width){
12363         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12364         //}
12365     },
12366     
12367     closeTriggerEl : function()
12368     {
12369         var close = this.el.select('.roo-combo-removable-btn', true).first();
12370         return close ? close : false;
12371     },
12372     
12373     removeBtnClick : function(e, h, el)
12374     {
12375         e.preventDefault();
12376         
12377         if(this.fireEvent("remove", this) !== false){
12378             this.reset();
12379             this.fireEvent("afterremove", this)
12380         }
12381     },
12382     
12383     createList : function()
12384     {
12385         this.list = Roo.get(document.body).createChild({
12386             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12387             cls: 'typeahead typeahead-long dropdown-menu shadow',
12388             style: 'display:none'
12389         });
12390         
12391         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12392         
12393     },
12394
12395     // private
12396     initTrigger : function(){
12397        
12398     },
12399
12400     // private
12401     onDestroy : function(){
12402         if(this.trigger){
12403             this.trigger.removeAllListeners();
12404           //  this.trigger.remove();
12405         }
12406         //if(this.wrap){
12407         //    this.wrap.remove();
12408         //}
12409         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12410     },
12411
12412     // private
12413     onFocus : function(){
12414         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12415         /*
12416         if(!this.mimicing){
12417             this.wrap.addClass('x-trigger-wrap-focus');
12418             this.mimicing = true;
12419             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12420             if(this.monitorTab){
12421                 this.el.on("keydown", this.checkTab, this);
12422             }
12423         }
12424         */
12425     },
12426
12427     // private
12428     checkTab : function(e){
12429         if(e.getKey() == e.TAB){
12430             this.triggerBlur();
12431         }
12432     },
12433
12434     // private
12435     onBlur : function(){
12436         // do nothing
12437     },
12438
12439     // private
12440     mimicBlur : function(e, t){
12441         /*
12442         if(!this.wrap.contains(t) && this.validateBlur()){
12443             this.triggerBlur();
12444         }
12445         */
12446     },
12447
12448     // private
12449     triggerBlur : function(){
12450         this.mimicing = false;
12451         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12452         if(this.monitorTab){
12453             this.el.un("keydown", this.checkTab, this);
12454         }
12455         //this.wrap.removeClass('x-trigger-wrap-focus');
12456         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12457     },
12458
12459     // private
12460     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12461     validateBlur : function(e, t){
12462         return true;
12463     },
12464
12465     // private
12466     onDisable : function(){
12467         this.inputEl().dom.disabled = true;
12468         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12469         //if(this.wrap){
12470         //    this.wrap.addClass('x-item-disabled');
12471         //}
12472     },
12473
12474     // private
12475     onEnable : function(){
12476         this.inputEl().dom.disabled = false;
12477         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12478         //if(this.wrap){
12479         //    this.el.removeClass('x-item-disabled');
12480         //}
12481     },
12482
12483     // private
12484     onShow : function(){
12485         var ae = this.getActionEl();
12486         
12487         if(ae){
12488             ae.dom.style.display = '';
12489             ae.dom.style.visibility = 'visible';
12490         }
12491     },
12492
12493     // private
12494     
12495     onHide : function(){
12496         var ae = this.getActionEl();
12497         ae.dom.style.display = 'none';
12498     },
12499
12500     /**
12501      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12502      * by an implementing function.
12503      * @method
12504      * @param {EventObject} e
12505      */
12506     onTriggerClick : Roo.emptyFn
12507 });
12508  
12509 /*
12510 * Licence: LGPL
12511 */
12512
12513 /**
12514  * @class Roo.bootstrap.CardUploader
12515  * @extends Roo.bootstrap.Button
12516  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12517  * @cfg {Number} errorTimeout default 3000
12518  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12519  * @cfg {Array}  html The button text.
12520
12521  *
12522  * @constructor
12523  * Create a new CardUploader
12524  * @param {Object} config The config object
12525  */
12526
12527 Roo.bootstrap.CardUploader = function(config){
12528     
12529  
12530     
12531     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12532     
12533     
12534     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12535         return r.data.id
12536      });
12537     
12538      this.addEvents({
12539          // raw events
12540         /**
12541          * @event preview
12542          * When a image is clicked on - and needs to display a slideshow or similar..
12543          * @param {Roo.bootstrap.Card} this
12544          * @param {Object} The image information data 
12545          *
12546          */
12547         'preview' : true,
12548          /**
12549          * @event download
12550          * When a the download link is clicked
12551          * @param {Roo.bootstrap.Card} this
12552          * @param {Object} The image information data  contains 
12553          */
12554         'download' : true
12555         
12556     });
12557 };
12558  
12559 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12560     
12561      
12562     errorTimeout : 3000,
12563      
12564     images : false,
12565    
12566     fileCollection : false,
12567     allowBlank : true,
12568     
12569     getAutoCreate : function()
12570     {
12571         
12572         var cfg =  {
12573             cls :'form-group' ,
12574             cn : [
12575                
12576                 {
12577                     tag: 'label',
12578                    //cls : 'input-group-addon',
12579                     html : this.fieldLabel
12580
12581                 },
12582
12583                 {
12584                     tag: 'input',
12585                     type : 'hidden',
12586                     name : this.name,
12587                     value : this.value,
12588                     cls : 'd-none  form-control'
12589                 },
12590                 
12591                 {
12592                     tag: 'input',
12593                     multiple : 'multiple',
12594                     type : 'file',
12595                     cls : 'd-none  roo-card-upload-selector'
12596                 },
12597                 
12598                 {
12599                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12600                 },
12601                 {
12602                     cls : 'card-columns roo-card-uploader-container'
12603                 }
12604
12605             ]
12606         };
12607            
12608          
12609         return cfg;
12610     },
12611     
12612     getChildContainer : function() /// what children are added to.
12613     {
12614         return this.containerEl;
12615     },
12616    
12617     getButtonContainer : function() /// what children are added to.
12618     {
12619         return this.el.select(".roo-card-uploader-button-container").first();
12620     },
12621    
12622     initEvents : function()
12623     {
12624         
12625         Roo.bootstrap.Input.prototype.initEvents.call(this);
12626         
12627         var t = this;
12628         this.addxtype({
12629             xns: Roo.bootstrap,
12630
12631             xtype : 'Button',
12632             container_method : 'getButtonContainer' ,            
12633             html :  this.html, // fix changable?
12634             cls : 'w-100 ',
12635             listeners : {
12636                 'click' : function(btn, e) {
12637                     t.onClick(e);
12638                 }
12639             }
12640         });
12641         
12642         
12643         
12644         
12645         this.urlAPI = (window.createObjectURL && window) || 
12646                                 (window.URL && URL.revokeObjectURL && URL) || 
12647                                 (window.webkitURL && webkitURL);
12648                         
12649          
12650          
12651          
12652         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12653         
12654         this.selectorEl.on('change', this.onFileSelected, this);
12655         if (this.images) {
12656             var t = this;
12657             this.images.forEach(function(img) {
12658                 t.addCard(img)
12659             });
12660             this.images = false;
12661         }
12662         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12663          
12664        
12665     },
12666     
12667    
12668     onClick : function(e)
12669     {
12670         e.preventDefault();
12671          
12672         this.selectorEl.dom.click();
12673          
12674     },
12675     
12676     onFileSelected : function(e)
12677     {
12678         e.preventDefault();
12679         
12680         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12681             return;
12682         }
12683         
12684         Roo.each(this.selectorEl.dom.files, function(file){    
12685             this.addFile(file);
12686         }, this);
12687          
12688     },
12689     
12690       
12691     
12692       
12693     
12694     addFile : function(file)
12695     {
12696            
12697         if(typeof(file) === 'string'){
12698             throw "Add file by name?"; // should not happen
12699             return;
12700         }
12701         
12702         if(!file || !this.urlAPI){
12703             return;
12704         }
12705         
12706         // file;
12707         // file.type;
12708         
12709         var _this = this;
12710         
12711         
12712         var url = _this.urlAPI.createObjectURL( file);
12713            
12714         this.addCard({
12715             id : Roo.bootstrap.CardUploader.ID--,
12716             is_uploaded : false,
12717             src : url,
12718             srcfile : file,
12719             title : file.name,
12720             mimetype : file.type,
12721             preview : false,
12722             is_deleted : 0
12723         });
12724         
12725     },
12726     
12727     /**
12728      * addCard - add an Attachment to the uploader
12729      * @param data - the data about the image to upload
12730      *
12731      * {
12732           id : 123
12733           title : "Title of file",
12734           is_uploaded : false,
12735           src : "http://.....",
12736           srcfile : { the File upload object },
12737           mimetype : file.type,
12738           preview : false,
12739           is_deleted : 0
12740           .. any other data...
12741         }
12742      *
12743      * 
12744     */
12745     
12746     addCard : function (data)
12747     {
12748         // hidden input element?
12749         // if the file is not an image...
12750         //then we need to use something other that and header_image
12751         var t = this;
12752         //   remove.....
12753         var footer = [
12754             {
12755                 xns : Roo.bootstrap,
12756                 xtype : 'CardFooter',
12757                  items: [
12758                     {
12759                         xns : Roo.bootstrap,
12760                         xtype : 'Element',
12761                         cls : 'd-flex',
12762                         items : [
12763                             
12764                             {
12765                                 xns : Roo.bootstrap,
12766                                 xtype : 'Button',
12767                                 html : String.format("<small>{0}</small>", data.title),
12768                                 cls : 'col-10 text-left',
12769                                 size: 'sm',
12770                                 weight: 'link',
12771                                 fa : 'download',
12772                                 listeners : {
12773                                     click : function() {
12774                                      
12775                                         t.fireEvent( "download", t, data );
12776                                     }
12777                                 }
12778                             },
12779                           
12780                             {
12781                                 xns : Roo.bootstrap,
12782                                 xtype : 'Button',
12783                                 style: 'max-height: 28px; ',
12784                                 size : 'sm',
12785                                 weight: 'danger',
12786                                 cls : 'col-2',
12787                                 fa : 'times',
12788                                 listeners : {
12789                                     click : function() {
12790                                         t.removeCard(data.id)
12791                                     }
12792                                 }
12793                             }
12794                         ]
12795                     }
12796                     
12797                 ] 
12798             }
12799             
12800         ];
12801         
12802         var cn = this.addxtype(
12803             {
12804                  
12805                 xns : Roo.bootstrap,
12806                 xtype : 'Card',
12807                 closeable : true,
12808                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12809                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12810                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12811                 data : data,
12812                 html : false,
12813                  
12814                 items : footer,
12815                 initEvents : function() {
12816                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12817                     var card = this;
12818                     this.imgEl = this.el.select('.card-img-top').first();
12819                     if (this.imgEl) {
12820                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12821                         this.imgEl.set({ 'pointer' : 'cursor' });
12822                                   
12823                     }
12824                     this.getCardFooter().addClass('p-1');
12825                     
12826                   
12827                 }
12828                 
12829             }
12830         );
12831         // dont' really need ot update items.
12832         // this.items.push(cn);
12833         this.fileCollection.add(cn);
12834         
12835         if (!data.srcfile) {
12836             this.updateInput();
12837             return;
12838         }
12839             
12840         var _t = this;
12841         var reader = new FileReader();
12842         reader.addEventListener("load", function() {  
12843             data.srcdata =  reader.result;
12844             _t.updateInput();
12845         });
12846         reader.readAsDataURL(data.srcfile);
12847         
12848         
12849         
12850     },
12851     removeCard : function(id)
12852     {
12853         
12854         var card  = this.fileCollection.get(id);
12855         card.data.is_deleted = 1;
12856         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12857         //this.fileCollection.remove(card);
12858         //this.items = this.items.filter(function(e) { return e != card });
12859         // dont' really need ot update items.
12860         card.el.dom.parentNode.removeChild(card.el.dom);
12861         this.updateInput();
12862
12863         
12864     },
12865     reset: function()
12866     {
12867         this.fileCollection.each(function(card) {
12868             if (card.el.dom && card.el.dom.parentNode) {
12869                 card.el.dom.parentNode.removeChild(card.el.dom);
12870             }
12871         });
12872         this.fileCollection.clear();
12873         this.updateInput();
12874     },
12875     
12876     updateInput : function()
12877     {
12878          var data = [];
12879         this.fileCollection.each(function(e) {
12880             data.push(e.data);
12881             
12882         });
12883         this.inputEl().dom.value = JSON.stringify(data);
12884         
12885         
12886         
12887     }
12888     
12889     
12890 });
12891
12892
12893 Roo.bootstrap.CardUploader.ID = -1;/*
12894  * Based on:
12895  * Ext JS Library 1.1.1
12896  * Copyright(c) 2006-2007, Ext JS, LLC.
12897  *
12898  * Originally Released Under LGPL - original licence link has changed is not relivant.
12899  *
12900  * Fork - LGPL
12901  * <script type="text/javascript">
12902  */
12903
12904
12905 /**
12906  * @class Roo.data.SortTypes
12907  * @singleton
12908  * Defines the default sorting (casting?) comparison functions used when sorting data.
12909  */
12910 Roo.data.SortTypes = {
12911     /**
12912      * Default sort that does nothing
12913      * @param {Mixed} s The value being converted
12914      * @return {Mixed} The comparison value
12915      */
12916     none : function(s){
12917         return s;
12918     },
12919     
12920     /**
12921      * The regular expression used to strip tags
12922      * @type {RegExp}
12923      * @property
12924      */
12925     stripTagsRE : /<\/?[^>]+>/gi,
12926     
12927     /**
12928      * Strips all HTML tags to sort on text only
12929      * @param {Mixed} s The value being converted
12930      * @return {String} The comparison value
12931      */
12932     asText : function(s){
12933         return String(s).replace(this.stripTagsRE, "");
12934     },
12935     
12936     /**
12937      * Strips all HTML tags to sort on text only - Case insensitive
12938      * @param {Mixed} s The value being converted
12939      * @return {String} The comparison value
12940      */
12941     asUCText : function(s){
12942         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12943     },
12944     
12945     /**
12946      * Case insensitive string
12947      * @param {Mixed} s The value being converted
12948      * @return {String} The comparison value
12949      */
12950     asUCString : function(s) {
12951         return String(s).toUpperCase();
12952     },
12953     
12954     /**
12955      * Date sorting
12956      * @param {Mixed} s The value being converted
12957      * @return {Number} The comparison value
12958      */
12959     asDate : function(s) {
12960         if(!s){
12961             return 0;
12962         }
12963         if(s instanceof Date){
12964             return s.getTime();
12965         }
12966         return Date.parse(String(s));
12967     },
12968     
12969     /**
12970      * Float sorting
12971      * @param {Mixed} s The value being converted
12972      * @return {Float} The comparison value
12973      */
12974     asFloat : function(s) {
12975         var val = parseFloat(String(s).replace(/,/g, ""));
12976         if(isNaN(val)) {
12977             val = 0;
12978         }
12979         return val;
12980     },
12981     
12982     /**
12983      * Integer sorting
12984      * @param {Mixed} s The value being converted
12985      * @return {Number} The comparison value
12986      */
12987     asInt : function(s) {
12988         var val = parseInt(String(s).replace(/,/g, ""));
12989         if(isNaN(val)) {
12990             val = 0;
12991         }
12992         return val;
12993     }
12994 };/*
12995  * Based on:
12996  * Ext JS Library 1.1.1
12997  * Copyright(c) 2006-2007, Ext JS, LLC.
12998  *
12999  * Originally Released Under LGPL - original licence link has changed is not relivant.
13000  *
13001  * Fork - LGPL
13002  * <script type="text/javascript">
13003  */
13004
13005 /**
13006 * @class Roo.data.Record
13007  * Instances of this class encapsulate both record <em>definition</em> information, and record
13008  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13009  * to access Records cached in an {@link Roo.data.Store} object.<br>
13010  * <p>
13011  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13012  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13013  * objects.<br>
13014  * <p>
13015  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13016  * @constructor
13017  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13018  * {@link #create}. The parameters are the same.
13019  * @param {Array} data An associative Array of data values keyed by the field name.
13020  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13021  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13022  * not specified an integer id is generated.
13023  */
13024 Roo.data.Record = function(data, id){
13025     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13026     this.data = data;
13027 };
13028
13029 /**
13030  * Generate a constructor for a specific record layout.
13031  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13032  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13033  * Each field definition object may contain the following properties: <ul>
13034  * <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,
13035  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13036  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13037  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13038  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13039  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13040  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13041  * this may be omitted.</p></li>
13042  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13043  * <ul><li>auto (Default, implies no conversion)</li>
13044  * <li>string</li>
13045  * <li>int</li>
13046  * <li>float</li>
13047  * <li>boolean</li>
13048  * <li>date</li></ul></p></li>
13049  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13050  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13051  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13052  * by the Reader into an object that will be stored in the Record. It is passed the
13053  * following parameters:<ul>
13054  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13055  * </ul></p></li>
13056  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13057  * </ul>
13058  * <br>usage:<br><pre><code>
13059 var TopicRecord = Roo.data.Record.create(
13060     {name: 'title', mapping: 'topic_title'},
13061     {name: 'author', mapping: 'username'},
13062     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13063     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13064     {name: 'lastPoster', mapping: 'user2'},
13065     {name: 'excerpt', mapping: 'post_text'}
13066 );
13067
13068 var myNewRecord = new TopicRecord({
13069     title: 'Do my job please',
13070     author: 'noobie',
13071     totalPosts: 1,
13072     lastPost: new Date(),
13073     lastPoster: 'Animal',
13074     excerpt: 'No way dude!'
13075 });
13076 myStore.add(myNewRecord);
13077 </code></pre>
13078  * @method create
13079  * @static
13080  */
13081 Roo.data.Record.create = function(o){
13082     var f = function(){
13083         f.superclass.constructor.apply(this, arguments);
13084     };
13085     Roo.extend(f, Roo.data.Record);
13086     var p = f.prototype;
13087     p.fields = new Roo.util.MixedCollection(false, function(field){
13088         return field.name;
13089     });
13090     for(var i = 0, len = o.length; i < len; i++){
13091         p.fields.add(new Roo.data.Field(o[i]));
13092     }
13093     f.getField = function(name){
13094         return p.fields.get(name);  
13095     };
13096     return f;
13097 };
13098
13099 Roo.data.Record.AUTO_ID = 1000;
13100 Roo.data.Record.EDIT = 'edit';
13101 Roo.data.Record.REJECT = 'reject';
13102 Roo.data.Record.COMMIT = 'commit';
13103
13104 Roo.data.Record.prototype = {
13105     /**
13106      * Readonly flag - true if this record has been modified.
13107      * @type Boolean
13108      */
13109     dirty : false,
13110     editing : false,
13111     error: null,
13112     modified: null,
13113
13114     // private
13115     join : function(store){
13116         this.store = store;
13117     },
13118
13119     /**
13120      * Set the named field to the specified value.
13121      * @param {String} name The name of the field to set.
13122      * @param {Object} value The value to set the field to.
13123      */
13124     set : function(name, value){
13125         if(this.data[name] == value){
13126             return;
13127         }
13128         this.dirty = true;
13129         if(!this.modified){
13130             this.modified = {};
13131         }
13132         if(typeof this.modified[name] == 'undefined'){
13133             this.modified[name] = this.data[name];
13134         }
13135         this.data[name] = value;
13136         if(!this.editing && this.store){
13137             this.store.afterEdit(this);
13138         }       
13139     },
13140
13141     /**
13142      * Get the value of the named field.
13143      * @param {String} name The name of the field to get the value of.
13144      * @return {Object} The value of the field.
13145      */
13146     get : function(name){
13147         return this.data[name]; 
13148     },
13149
13150     // private
13151     beginEdit : function(){
13152         this.editing = true;
13153         this.modified = {}; 
13154     },
13155
13156     // private
13157     cancelEdit : function(){
13158         this.editing = false;
13159         delete this.modified;
13160     },
13161
13162     // private
13163     endEdit : function(){
13164         this.editing = false;
13165         if(this.dirty && this.store){
13166             this.store.afterEdit(this);
13167         }
13168     },
13169
13170     /**
13171      * Usually called by the {@link Roo.data.Store} which owns the Record.
13172      * Rejects all changes made to the Record since either creation, or the last commit operation.
13173      * Modified fields are reverted to their original values.
13174      * <p>
13175      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13176      * of reject operations.
13177      */
13178     reject : function(){
13179         var m = this.modified;
13180         for(var n in m){
13181             if(typeof m[n] != "function"){
13182                 this.data[n] = m[n];
13183             }
13184         }
13185         this.dirty = false;
13186         delete this.modified;
13187         this.editing = false;
13188         if(this.store){
13189             this.store.afterReject(this);
13190         }
13191     },
13192
13193     /**
13194      * Usually called by the {@link Roo.data.Store} which owns the Record.
13195      * Commits all changes made to the Record since either creation, or the last commit operation.
13196      * <p>
13197      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13198      * of commit operations.
13199      */
13200     commit : function(){
13201         this.dirty = false;
13202         delete this.modified;
13203         this.editing = false;
13204         if(this.store){
13205             this.store.afterCommit(this);
13206         }
13207     },
13208
13209     // private
13210     hasError : function(){
13211         return this.error != null;
13212     },
13213
13214     // private
13215     clearError : function(){
13216         this.error = null;
13217     },
13218
13219     /**
13220      * Creates a copy of this record.
13221      * @param {String} id (optional) A new record id if you don't want to use this record's id
13222      * @return {Record}
13223      */
13224     copy : function(newId) {
13225         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13226     }
13227 };/*
13228  * Based on:
13229  * Ext JS Library 1.1.1
13230  * Copyright(c) 2006-2007, Ext JS, LLC.
13231  *
13232  * Originally Released Under LGPL - original licence link has changed is not relivant.
13233  *
13234  * Fork - LGPL
13235  * <script type="text/javascript">
13236  */
13237
13238
13239
13240 /**
13241  * @class Roo.data.Store
13242  * @extends Roo.util.Observable
13243  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13244  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13245  * <p>
13246  * 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
13247  * has no knowledge of the format of the data returned by the Proxy.<br>
13248  * <p>
13249  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13250  * instances from the data object. These records are cached and made available through accessor functions.
13251  * @constructor
13252  * Creates a new Store.
13253  * @param {Object} config A config object containing the objects needed for the Store to access data,
13254  * and read the data into Records.
13255  */
13256 Roo.data.Store = function(config){
13257     this.data = new Roo.util.MixedCollection(false);
13258     this.data.getKey = function(o){
13259         return o.id;
13260     };
13261     this.baseParams = {};
13262     // private
13263     this.paramNames = {
13264         "start" : "start",
13265         "limit" : "limit",
13266         "sort" : "sort",
13267         "dir" : "dir",
13268         "multisort" : "_multisort"
13269     };
13270
13271     if(config && config.data){
13272         this.inlineData = config.data;
13273         delete config.data;
13274     }
13275
13276     Roo.apply(this, config);
13277     
13278     if(this.reader){ // reader passed
13279         this.reader = Roo.factory(this.reader, Roo.data);
13280         this.reader.xmodule = this.xmodule || false;
13281         if(!this.recordType){
13282             this.recordType = this.reader.recordType;
13283         }
13284         if(this.reader.onMetaChange){
13285             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13286         }
13287     }
13288
13289     if(this.recordType){
13290         this.fields = this.recordType.prototype.fields;
13291     }
13292     this.modified = [];
13293
13294     this.addEvents({
13295         /**
13296          * @event datachanged
13297          * Fires when the data cache has changed, and a widget which is using this Store
13298          * as a Record cache should refresh its view.
13299          * @param {Store} this
13300          */
13301         datachanged : true,
13302         /**
13303          * @event metachange
13304          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13305          * @param {Store} this
13306          * @param {Object} meta The JSON metadata
13307          */
13308         metachange : true,
13309         /**
13310          * @event add
13311          * Fires when Records have been added to the Store
13312          * @param {Store} this
13313          * @param {Roo.data.Record[]} records The array of Records added
13314          * @param {Number} index The index at which the record(s) were added
13315          */
13316         add : true,
13317         /**
13318          * @event remove
13319          * Fires when a Record has been removed from the Store
13320          * @param {Store} this
13321          * @param {Roo.data.Record} record The Record that was removed
13322          * @param {Number} index The index at which the record was removed
13323          */
13324         remove : true,
13325         /**
13326          * @event update
13327          * Fires when a Record has been updated
13328          * @param {Store} this
13329          * @param {Roo.data.Record} record The Record that was updated
13330          * @param {String} operation The update operation being performed.  Value may be one of:
13331          * <pre><code>
13332  Roo.data.Record.EDIT
13333  Roo.data.Record.REJECT
13334  Roo.data.Record.COMMIT
13335          * </code></pre>
13336          */
13337         update : true,
13338         /**
13339          * @event clear
13340          * Fires when the data cache has been cleared.
13341          * @param {Store} this
13342          */
13343         clear : true,
13344         /**
13345          * @event beforeload
13346          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13347          * the load action will be canceled.
13348          * @param {Store} this
13349          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13350          */
13351         beforeload : true,
13352         /**
13353          * @event beforeloadadd
13354          * Fires after a new set of Records has been loaded.
13355          * @param {Store} this
13356          * @param {Roo.data.Record[]} records The Records that were loaded
13357          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13358          */
13359         beforeloadadd : true,
13360         /**
13361          * @event load
13362          * Fires after a new set of Records has been loaded, before they are added to the store.
13363          * @param {Store} this
13364          * @param {Roo.data.Record[]} records The Records that were loaded
13365          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13366          * @params {Object} return from reader
13367          */
13368         load : true,
13369         /**
13370          * @event loadexception
13371          * Fires if an exception occurs in the Proxy during loading.
13372          * Called with the signature of the Proxy's "loadexception" event.
13373          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13374          * 
13375          * @param {Proxy} 
13376          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13377          * @param {Object} load options 
13378          * @param {Object} jsonData from your request (normally this contains the Exception)
13379          */
13380         loadexception : true
13381     });
13382     
13383     if(this.proxy){
13384         this.proxy = Roo.factory(this.proxy, Roo.data);
13385         this.proxy.xmodule = this.xmodule || false;
13386         this.relayEvents(this.proxy,  ["loadexception"]);
13387     }
13388     this.sortToggle = {};
13389     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13390
13391     Roo.data.Store.superclass.constructor.call(this);
13392
13393     if(this.inlineData){
13394         this.loadData(this.inlineData);
13395         delete this.inlineData;
13396     }
13397 };
13398
13399 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13400      /**
13401     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13402     * without a remote query - used by combo/forms at present.
13403     */
13404     
13405     /**
13406     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13407     */
13408     /**
13409     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13410     */
13411     /**
13412     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13413     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13414     */
13415     /**
13416     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13417     * on any HTTP request
13418     */
13419     /**
13420     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13421     */
13422     /**
13423     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13424     */
13425     multiSort: false,
13426     /**
13427     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13428     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13429     */
13430     remoteSort : false,
13431
13432     /**
13433     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13434      * loaded or when a record is removed. (defaults to false).
13435     */
13436     pruneModifiedRecords : false,
13437
13438     // private
13439     lastOptions : null,
13440
13441     /**
13442      * Add Records to the Store and fires the add event.
13443      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13444      */
13445     add : function(records){
13446         records = [].concat(records);
13447         for(var i = 0, len = records.length; i < len; i++){
13448             records[i].join(this);
13449         }
13450         var index = this.data.length;
13451         this.data.addAll(records);
13452         this.fireEvent("add", this, records, index);
13453     },
13454
13455     /**
13456      * Remove a Record from the Store and fires the remove event.
13457      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13458      */
13459     remove : function(record){
13460         var index = this.data.indexOf(record);
13461         this.data.removeAt(index);
13462  
13463         if(this.pruneModifiedRecords){
13464             this.modified.remove(record);
13465         }
13466         this.fireEvent("remove", this, record, index);
13467     },
13468
13469     /**
13470      * Remove all Records from the Store and fires the clear event.
13471      */
13472     removeAll : function(){
13473         this.data.clear();
13474         if(this.pruneModifiedRecords){
13475             this.modified = [];
13476         }
13477         this.fireEvent("clear", this);
13478     },
13479
13480     /**
13481      * Inserts Records to the Store at the given index and fires the add event.
13482      * @param {Number} index The start index at which to insert the passed Records.
13483      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13484      */
13485     insert : function(index, records){
13486         records = [].concat(records);
13487         for(var i = 0, len = records.length; i < len; i++){
13488             this.data.insert(index, records[i]);
13489             records[i].join(this);
13490         }
13491         this.fireEvent("add", this, records, index);
13492     },
13493
13494     /**
13495      * Get the index within the cache of the passed Record.
13496      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13497      * @return {Number} The index of the passed Record. Returns -1 if not found.
13498      */
13499     indexOf : function(record){
13500         return this.data.indexOf(record);
13501     },
13502
13503     /**
13504      * Get the index within the cache of the Record with the passed id.
13505      * @param {String} id The id of the Record to find.
13506      * @return {Number} The index of the Record. Returns -1 if not found.
13507      */
13508     indexOfId : function(id){
13509         return this.data.indexOfKey(id);
13510     },
13511
13512     /**
13513      * Get the Record with the specified id.
13514      * @param {String} id The id of the Record to find.
13515      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13516      */
13517     getById : function(id){
13518         return this.data.key(id);
13519     },
13520
13521     /**
13522      * Get the Record at the specified index.
13523      * @param {Number} index The index of the Record to find.
13524      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13525      */
13526     getAt : function(index){
13527         return this.data.itemAt(index);
13528     },
13529
13530     /**
13531      * Returns a range of Records between specified indices.
13532      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13533      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13534      * @return {Roo.data.Record[]} An array of Records
13535      */
13536     getRange : function(start, end){
13537         return this.data.getRange(start, end);
13538     },
13539
13540     // private
13541     storeOptions : function(o){
13542         o = Roo.apply({}, o);
13543         delete o.callback;
13544         delete o.scope;
13545         this.lastOptions = o;
13546     },
13547
13548     /**
13549      * Loads the Record cache from the configured Proxy using the configured Reader.
13550      * <p>
13551      * If using remote paging, then the first load call must specify the <em>start</em>
13552      * and <em>limit</em> properties in the options.params property to establish the initial
13553      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13554      * <p>
13555      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13556      * and this call will return before the new data has been loaded. Perform any post-processing
13557      * in a callback function, or in a "load" event handler.</strong>
13558      * <p>
13559      * @param {Object} options An object containing properties which control loading options:<ul>
13560      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13561      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13562      * passed the following arguments:<ul>
13563      * <li>r : Roo.data.Record[]</li>
13564      * <li>options: Options object from the load call</li>
13565      * <li>success: Boolean success indicator</li></ul></li>
13566      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13567      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13568      * </ul>
13569      */
13570     load : function(options){
13571         options = options || {};
13572         if(this.fireEvent("beforeload", this, options) !== false){
13573             this.storeOptions(options);
13574             var p = Roo.apply(options.params || {}, this.baseParams);
13575             // if meta was not loaded from remote source.. try requesting it.
13576             if (!this.reader.metaFromRemote) {
13577                 p._requestMeta = 1;
13578             }
13579             if(this.sortInfo && this.remoteSort){
13580                 var pn = this.paramNames;
13581                 p[pn["sort"]] = this.sortInfo.field;
13582                 p[pn["dir"]] = this.sortInfo.direction;
13583             }
13584             if (this.multiSort) {
13585                 var pn = this.paramNames;
13586                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13587             }
13588             
13589             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13590         }
13591     },
13592
13593     /**
13594      * Reloads the Record cache from the configured Proxy using the configured Reader and
13595      * the options from the last load operation performed.
13596      * @param {Object} options (optional) An object containing properties which may override the options
13597      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13598      * the most recently used options are reused).
13599      */
13600     reload : function(options){
13601         this.load(Roo.applyIf(options||{}, this.lastOptions));
13602     },
13603
13604     // private
13605     // Called as a callback by the Reader during a load operation.
13606     loadRecords : function(o, options, success){
13607         if(!o || success === false){
13608             if(success !== false){
13609                 this.fireEvent("load", this, [], options, o);
13610             }
13611             if(options.callback){
13612                 options.callback.call(options.scope || this, [], options, false);
13613             }
13614             return;
13615         }
13616         // if data returned failure - throw an exception.
13617         if (o.success === false) {
13618             // show a message if no listener is registered.
13619             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13620                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13621             }
13622             // loadmask wil be hooked into this..
13623             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13624             return;
13625         }
13626         var r = o.records, t = o.totalRecords || r.length;
13627         
13628         this.fireEvent("beforeloadadd", this, r, options, o);
13629         
13630         if(!options || options.add !== true){
13631             if(this.pruneModifiedRecords){
13632                 this.modified = [];
13633             }
13634             for(var i = 0, len = r.length; i < len; i++){
13635                 r[i].join(this);
13636             }
13637             if(this.snapshot){
13638                 this.data = this.snapshot;
13639                 delete this.snapshot;
13640             }
13641             this.data.clear();
13642             this.data.addAll(r);
13643             this.totalLength = t;
13644             this.applySort();
13645             this.fireEvent("datachanged", this);
13646         }else{
13647             this.totalLength = Math.max(t, this.data.length+r.length);
13648             this.add(r);
13649         }
13650         
13651         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13652                 
13653             var e = new Roo.data.Record({});
13654
13655             e.set(this.parent.displayField, this.parent.emptyTitle);
13656             e.set(this.parent.valueField, '');
13657
13658             this.insert(0, e);
13659         }
13660             
13661         this.fireEvent("load", this, r, options, o);
13662         if(options.callback){
13663             options.callback.call(options.scope || this, r, options, true);
13664         }
13665     },
13666
13667
13668     /**
13669      * Loads data from a passed data block. A Reader which understands the format of the data
13670      * must have been configured in the constructor.
13671      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13672      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13673      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13674      */
13675     loadData : function(o, append){
13676         var r = this.reader.readRecords(o);
13677         this.loadRecords(r, {add: append}, true);
13678     },
13679     
13680      /**
13681      * using 'cn' the nested child reader read the child array into it's child stores.
13682      * @param {Object} rec The record with a 'children array
13683      */
13684     loadDataFromChildren : function(rec)
13685     {
13686         this.loadData(this.reader.toLoadData(rec));
13687     },
13688     
13689
13690     /**
13691      * Gets the number of cached records.
13692      * <p>
13693      * <em>If using paging, this may not be the total size of the dataset. If the data object
13694      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13695      * the data set size</em>
13696      */
13697     getCount : function(){
13698         return this.data.length || 0;
13699     },
13700
13701     /**
13702      * Gets the total number of records in the dataset as returned by the server.
13703      * <p>
13704      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13705      * the dataset size</em>
13706      */
13707     getTotalCount : function(){
13708         return this.totalLength || 0;
13709     },
13710
13711     /**
13712      * Returns the sort state of the Store as an object with two properties:
13713      * <pre><code>
13714  field {String} The name of the field by which the Records are sorted
13715  direction {String} The sort order, "ASC" or "DESC"
13716      * </code></pre>
13717      */
13718     getSortState : function(){
13719         return this.sortInfo;
13720     },
13721
13722     // private
13723     applySort : function(){
13724         if(this.sortInfo && !this.remoteSort){
13725             var s = this.sortInfo, f = s.field;
13726             var st = this.fields.get(f).sortType;
13727             var fn = function(r1, r2){
13728                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13729                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13730             };
13731             this.data.sort(s.direction, fn);
13732             if(this.snapshot && this.snapshot != this.data){
13733                 this.snapshot.sort(s.direction, fn);
13734             }
13735         }
13736     },
13737
13738     /**
13739      * Sets the default sort column and order to be used by the next load operation.
13740      * @param {String} fieldName The name of the field to sort by.
13741      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13742      */
13743     setDefaultSort : function(field, dir){
13744         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13745     },
13746
13747     /**
13748      * Sort the Records.
13749      * If remote sorting is used, the sort is performed on the server, and the cache is
13750      * reloaded. If local sorting is used, the cache is sorted internally.
13751      * @param {String} fieldName The name of the field to sort by.
13752      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13753      */
13754     sort : function(fieldName, dir){
13755         var f = this.fields.get(fieldName);
13756         if(!dir){
13757             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13758             
13759             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13760                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13761             }else{
13762                 dir = f.sortDir;
13763             }
13764         }
13765         this.sortToggle[f.name] = dir;
13766         this.sortInfo = {field: f.name, direction: dir};
13767         if(!this.remoteSort){
13768             this.applySort();
13769             this.fireEvent("datachanged", this);
13770         }else{
13771             this.load(this.lastOptions);
13772         }
13773     },
13774
13775     /**
13776      * Calls the specified function for each of the Records in the cache.
13777      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13778      * Returning <em>false</em> aborts and exits the iteration.
13779      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13780      */
13781     each : function(fn, scope){
13782         this.data.each(fn, scope);
13783     },
13784
13785     /**
13786      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13787      * (e.g., during paging).
13788      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13789      */
13790     getModifiedRecords : function(){
13791         return this.modified;
13792     },
13793
13794     // private
13795     createFilterFn : function(property, value, anyMatch){
13796         if(!value.exec){ // not a regex
13797             value = String(value);
13798             if(value.length == 0){
13799                 return false;
13800             }
13801             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13802         }
13803         return function(r){
13804             return value.test(r.data[property]);
13805         };
13806     },
13807
13808     /**
13809      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13810      * @param {String} property A field on your records
13811      * @param {Number} start The record index to start at (defaults to 0)
13812      * @param {Number} end The last record index to include (defaults to length - 1)
13813      * @return {Number} The sum
13814      */
13815     sum : function(property, start, end){
13816         var rs = this.data.items, v = 0;
13817         start = start || 0;
13818         end = (end || end === 0) ? end : rs.length-1;
13819
13820         for(var i = start; i <= end; i++){
13821             v += (rs[i].data[property] || 0);
13822         }
13823         return v;
13824     },
13825
13826     /**
13827      * Filter the records by a specified property.
13828      * @param {String} field A field on your records
13829      * @param {String/RegExp} value Either a string that the field
13830      * should start with or a RegExp to test against the field
13831      * @param {Boolean} anyMatch True to match any part not just the beginning
13832      */
13833     filter : function(property, value, anyMatch){
13834         var fn = this.createFilterFn(property, value, anyMatch);
13835         return fn ? this.filterBy(fn) : this.clearFilter();
13836     },
13837
13838     /**
13839      * Filter 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      * otherwise it is filtered.
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      */
13845     filterBy : function(fn, scope){
13846         this.snapshot = this.snapshot || this.data;
13847         this.data = this.queryBy(fn, scope||this);
13848         this.fireEvent("datachanged", this);
13849     },
13850
13851     /**
13852      * Query the records by a specified property.
13853      * @param {String} field A field on your records
13854      * @param {String/RegExp} value Either a string that the field
13855      * should start with or a RegExp to test against the field
13856      * @param {Boolean} anyMatch True to match any part not just the beginning
13857      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13858      */
13859     query : function(property, value, anyMatch){
13860         var fn = this.createFilterFn(property, value, anyMatch);
13861         return fn ? this.queryBy(fn) : this.data.clone();
13862     },
13863
13864     /**
13865      * Query by a function. The specified function will be called with each
13866      * record in this data source. If the function returns true the record is included
13867      * in the results.
13868      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13869      * @param {Object} scope (optional) The scope of the function (defaults to this)
13870       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13871      **/
13872     queryBy : function(fn, scope){
13873         var data = this.snapshot || this.data;
13874         return data.filterBy(fn, scope||this);
13875     },
13876
13877     /**
13878      * Collects unique values for a particular dataIndex from this store.
13879      * @param {String} dataIndex The property to collect
13880      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13881      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13882      * @return {Array} An array of the unique values
13883      **/
13884     collect : function(dataIndex, allowNull, bypassFilter){
13885         var d = (bypassFilter === true && this.snapshot) ?
13886                 this.snapshot.items : this.data.items;
13887         var v, sv, r = [], l = {};
13888         for(var i = 0, len = d.length; i < len; i++){
13889             v = d[i].data[dataIndex];
13890             sv = String(v);
13891             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13892                 l[sv] = true;
13893                 r[r.length] = v;
13894             }
13895         }
13896         return r;
13897     },
13898
13899     /**
13900      * Revert to a view of the Record cache with no filtering applied.
13901      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13902      */
13903     clearFilter : function(suppressEvent){
13904         if(this.snapshot && this.snapshot != this.data){
13905             this.data = this.snapshot;
13906             delete this.snapshot;
13907             if(suppressEvent !== true){
13908                 this.fireEvent("datachanged", this);
13909             }
13910         }
13911     },
13912
13913     // private
13914     afterEdit : function(record){
13915         if(this.modified.indexOf(record) == -1){
13916             this.modified.push(record);
13917         }
13918         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13919     },
13920     
13921     // private
13922     afterReject : function(record){
13923         this.modified.remove(record);
13924         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13925     },
13926
13927     // private
13928     afterCommit : function(record){
13929         this.modified.remove(record);
13930         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13931     },
13932
13933     /**
13934      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13935      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13936      */
13937     commitChanges : function(){
13938         var m = this.modified.slice(0);
13939         this.modified = [];
13940         for(var i = 0, len = m.length; i < len; i++){
13941             m[i].commit();
13942         }
13943     },
13944
13945     /**
13946      * Cancel outstanding changes on all changed records.
13947      */
13948     rejectChanges : function(){
13949         var m = this.modified.slice(0);
13950         this.modified = [];
13951         for(var i = 0, len = m.length; i < len; i++){
13952             m[i].reject();
13953         }
13954     },
13955
13956     onMetaChange : function(meta, rtype, o){
13957         this.recordType = rtype;
13958         this.fields = rtype.prototype.fields;
13959         delete this.snapshot;
13960         this.sortInfo = meta.sortInfo || this.sortInfo;
13961         this.modified = [];
13962         this.fireEvent('metachange', this, this.reader.meta);
13963     },
13964     
13965     moveIndex : function(data, type)
13966     {
13967         var index = this.indexOf(data);
13968         
13969         var newIndex = index + type;
13970         
13971         this.remove(data);
13972         
13973         this.insert(newIndex, data);
13974         
13975     }
13976 });/*
13977  * Based on:
13978  * Ext JS Library 1.1.1
13979  * Copyright(c) 2006-2007, Ext JS, LLC.
13980  *
13981  * Originally Released Under LGPL - original licence link has changed is not relivant.
13982  *
13983  * Fork - LGPL
13984  * <script type="text/javascript">
13985  */
13986
13987 /**
13988  * @class Roo.data.SimpleStore
13989  * @extends Roo.data.Store
13990  * Small helper class to make creating Stores from Array data easier.
13991  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13992  * @cfg {Array} fields An array of field definition objects, or field name strings.
13993  * @cfg {Object} an existing reader (eg. copied from another store)
13994  * @cfg {Array} data The multi-dimensional array of data
13995  * @constructor
13996  * @param {Object} config
13997  */
13998 Roo.data.SimpleStore = function(config)
13999 {
14000     Roo.data.SimpleStore.superclass.constructor.call(this, {
14001         isLocal : true,
14002         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14003                 id: config.id
14004             },
14005             Roo.data.Record.create(config.fields)
14006         ),
14007         proxy : new Roo.data.MemoryProxy(config.data)
14008     });
14009     this.load();
14010 };
14011 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14012  * Based on:
14013  * Ext JS Library 1.1.1
14014  * Copyright(c) 2006-2007, Ext JS, LLC.
14015  *
14016  * Originally Released Under LGPL - original licence link has changed is not relivant.
14017  *
14018  * Fork - LGPL
14019  * <script type="text/javascript">
14020  */
14021
14022 /**
14023 /**
14024  * @extends Roo.data.Store
14025  * @class Roo.data.JsonStore
14026  * Small helper class to make creating Stores for JSON data easier. <br/>
14027 <pre><code>
14028 var store = new Roo.data.JsonStore({
14029     url: 'get-images.php',
14030     root: 'images',
14031     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14032 });
14033 </code></pre>
14034  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14035  * JsonReader and HttpProxy (unless inline data is provided).</b>
14036  * @cfg {Array} fields An array of field definition objects, or field name strings.
14037  * @constructor
14038  * @param {Object} config
14039  */
14040 Roo.data.JsonStore = function(c){
14041     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14042         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14043         reader: new Roo.data.JsonReader(c, c.fields)
14044     }));
14045 };
14046 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14047  * Based on:
14048  * Ext JS Library 1.1.1
14049  * Copyright(c) 2006-2007, Ext JS, LLC.
14050  *
14051  * Originally Released Under LGPL - original licence link has changed is not relivant.
14052  *
14053  * Fork - LGPL
14054  * <script type="text/javascript">
14055  */
14056
14057  
14058 Roo.data.Field = function(config){
14059     if(typeof config == "string"){
14060         config = {name: config};
14061     }
14062     Roo.apply(this, config);
14063     
14064     if(!this.type){
14065         this.type = "auto";
14066     }
14067     
14068     var st = Roo.data.SortTypes;
14069     // named sortTypes are supported, here we look them up
14070     if(typeof this.sortType == "string"){
14071         this.sortType = st[this.sortType];
14072     }
14073     
14074     // set default sortType for strings and dates
14075     if(!this.sortType){
14076         switch(this.type){
14077             case "string":
14078                 this.sortType = st.asUCString;
14079                 break;
14080             case "date":
14081                 this.sortType = st.asDate;
14082                 break;
14083             default:
14084                 this.sortType = st.none;
14085         }
14086     }
14087
14088     // define once
14089     var stripRe = /[\$,%]/g;
14090
14091     // prebuilt conversion function for this field, instead of
14092     // switching every time we're reading a value
14093     if(!this.convert){
14094         var cv, dateFormat = this.dateFormat;
14095         switch(this.type){
14096             case "":
14097             case "auto":
14098             case undefined:
14099                 cv = function(v){ return v; };
14100                 break;
14101             case "string":
14102                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14103                 break;
14104             case "int":
14105                 cv = function(v){
14106                     return v !== undefined && v !== null && v !== '' ?
14107                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14108                     };
14109                 break;
14110             case "float":
14111                 cv = function(v){
14112                     return v !== undefined && v !== null && v !== '' ?
14113                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14114                     };
14115                 break;
14116             case "bool":
14117             case "boolean":
14118                 cv = function(v){ return v === true || v === "true" || v == 1; };
14119                 break;
14120             case "date":
14121                 cv = function(v){
14122                     if(!v){
14123                         return '';
14124                     }
14125                     if(v instanceof Date){
14126                         return v;
14127                     }
14128                     if(dateFormat){
14129                         if(dateFormat == "timestamp"){
14130                             return new Date(v*1000);
14131                         }
14132                         return Date.parseDate(v, dateFormat);
14133                     }
14134                     var parsed = Date.parse(v);
14135                     return parsed ? new Date(parsed) : null;
14136                 };
14137              break;
14138             
14139         }
14140         this.convert = cv;
14141     }
14142 };
14143
14144 Roo.data.Field.prototype = {
14145     dateFormat: null,
14146     defaultValue: "",
14147     mapping: null,
14148     sortType : null,
14149     sortDir : "ASC"
14150 };/*
14151  * Based on:
14152  * Ext JS Library 1.1.1
14153  * Copyright(c) 2006-2007, Ext JS, LLC.
14154  *
14155  * Originally Released Under LGPL - original licence link has changed is not relivant.
14156  *
14157  * Fork - LGPL
14158  * <script type="text/javascript">
14159  */
14160  
14161 // Base class for reading structured data from a data source.  This class is intended to be
14162 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14163
14164 /**
14165  * @class Roo.data.DataReader
14166  * Base class for reading structured data from a data source.  This class is intended to be
14167  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14168  */
14169
14170 Roo.data.DataReader = function(meta, recordType){
14171     
14172     this.meta = meta;
14173     
14174     this.recordType = recordType instanceof Array ? 
14175         Roo.data.Record.create(recordType) : recordType;
14176 };
14177
14178 Roo.data.DataReader.prototype = {
14179     
14180     
14181     readerType : 'Data',
14182      /**
14183      * Create an empty record
14184      * @param {Object} data (optional) - overlay some values
14185      * @return {Roo.data.Record} record created.
14186      */
14187     newRow :  function(d) {
14188         var da =  {};
14189         this.recordType.prototype.fields.each(function(c) {
14190             switch( c.type) {
14191                 case 'int' : da[c.name] = 0; break;
14192                 case 'date' : da[c.name] = new Date(); break;
14193                 case 'float' : da[c.name] = 0.0; break;
14194                 case 'boolean' : da[c.name] = false; break;
14195                 default : da[c.name] = ""; break;
14196             }
14197             
14198         });
14199         return new this.recordType(Roo.apply(da, d));
14200     }
14201     
14202     
14203 };/*
14204  * Based on:
14205  * Ext JS Library 1.1.1
14206  * Copyright(c) 2006-2007, Ext JS, LLC.
14207  *
14208  * Originally Released Under LGPL - original licence link has changed is not relivant.
14209  *
14210  * Fork - LGPL
14211  * <script type="text/javascript">
14212  */
14213
14214 /**
14215  * @class Roo.data.DataProxy
14216  * @extends Roo.data.Observable
14217  * This class is an abstract base class for implementations which provide retrieval of
14218  * unformatted data objects.<br>
14219  * <p>
14220  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14221  * (of the appropriate type which knows how to parse the data object) to provide a block of
14222  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14223  * <p>
14224  * Custom implementations must implement the load method as described in
14225  * {@link Roo.data.HttpProxy#load}.
14226  */
14227 Roo.data.DataProxy = function(){
14228     this.addEvents({
14229         /**
14230          * @event beforeload
14231          * Fires before a network request is made to retrieve a data object.
14232          * @param {Object} This DataProxy object.
14233          * @param {Object} params The params parameter to the load function.
14234          */
14235         beforeload : true,
14236         /**
14237          * @event load
14238          * Fires before the load method's callback is called.
14239          * @param {Object} This DataProxy object.
14240          * @param {Object} o The data object.
14241          * @param {Object} arg The callback argument object passed to the load function.
14242          */
14243         load : true,
14244         /**
14245          * @event loadexception
14246          * Fires if an Exception occurs during data retrieval.
14247          * @param {Object} This DataProxy object.
14248          * @param {Object} o The data object.
14249          * @param {Object} arg The callback argument object passed to the load function.
14250          * @param {Object} e The Exception.
14251          */
14252         loadexception : true
14253     });
14254     Roo.data.DataProxy.superclass.constructor.call(this);
14255 };
14256
14257 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14258
14259     /**
14260      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14261      */
14262 /*
14263  * Based on:
14264  * Ext JS Library 1.1.1
14265  * Copyright(c) 2006-2007, Ext JS, LLC.
14266  *
14267  * Originally Released Under LGPL - original licence link has changed is not relivant.
14268  *
14269  * Fork - LGPL
14270  * <script type="text/javascript">
14271  */
14272 /**
14273  * @class Roo.data.MemoryProxy
14274  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14275  * to the Reader when its load method is called.
14276  * @constructor
14277  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14278  */
14279 Roo.data.MemoryProxy = function(data){
14280     if (data.data) {
14281         data = data.data;
14282     }
14283     Roo.data.MemoryProxy.superclass.constructor.call(this);
14284     this.data = data;
14285 };
14286
14287 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14288     
14289     /**
14290      * Load data from the requested source (in this case an in-memory
14291      * data object passed to the constructor), read the data object into
14292      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14293      * process that block using the passed callback.
14294      * @param {Object} params This parameter is not used by the MemoryProxy class.
14295      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14296      * object into a block of Roo.data.Records.
14297      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14298      * The function must be passed <ul>
14299      * <li>The Record block object</li>
14300      * <li>The "arg" argument from the load function</li>
14301      * <li>A boolean success indicator</li>
14302      * </ul>
14303      * @param {Object} scope The scope in which to call the callback
14304      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14305      */
14306     load : function(params, reader, callback, scope, arg){
14307         params = params || {};
14308         var result;
14309         try {
14310             result = reader.readRecords(params.data ? params.data :this.data);
14311         }catch(e){
14312             this.fireEvent("loadexception", this, arg, null, e);
14313             callback.call(scope, null, arg, false);
14314             return;
14315         }
14316         callback.call(scope, result, arg, true);
14317     },
14318     
14319     // private
14320     update : function(params, records){
14321         
14322     }
14323 });/*
14324  * Based on:
14325  * Ext JS Library 1.1.1
14326  * Copyright(c) 2006-2007, Ext JS, LLC.
14327  *
14328  * Originally Released Under LGPL - original licence link has changed is not relivant.
14329  *
14330  * Fork - LGPL
14331  * <script type="text/javascript">
14332  */
14333 /**
14334  * @class Roo.data.HttpProxy
14335  * @extends Roo.data.DataProxy
14336  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14337  * configured to reference a certain URL.<br><br>
14338  * <p>
14339  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14340  * from which the running page was served.<br><br>
14341  * <p>
14342  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14343  * <p>
14344  * Be aware that to enable the browser to parse an XML document, the server must set
14345  * the Content-Type header in the HTTP response to "text/xml".
14346  * @constructor
14347  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14348  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14349  * will be used to make the request.
14350  */
14351 Roo.data.HttpProxy = function(conn){
14352     Roo.data.HttpProxy.superclass.constructor.call(this);
14353     // is conn a conn config or a real conn?
14354     this.conn = conn;
14355     this.useAjax = !conn || !conn.events;
14356   
14357 };
14358
14359 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14360     // thse are take from connection...
14361     
14362     /**
14363      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14364      */
14365     /**
14366      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14367      * extra parameters to each request made by this object. (defaults to undefined)
14368      */
14369     /**
14370      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14371      *  to each request made by this object. (defaults to undefined)
14372      */
14373     /**
14374      * @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)
14375      */
14376     /**
14377      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14378      */
14379      /**
14380      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14381      * @type Boolean
14382      */
14383   
14384
14385     /**
14386      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14387      * @type Boolean
14388      */
14389     /**
14390      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14391      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14392      * a finer-grained basis than the DataProxy events.
14393      */
14394     getConnection : function(){
14395         return this.useAjax ? Roo.Ajax : this.conn;
14396     },
14397
14398     /**
14399      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14400      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14401      * process that block using the passed callback.
14402      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14403      * for the request to the remote server.
14404      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14405      * object into a block of Roo.data.Records.
14406      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14407      * The function must be passed <ul>
14408      * <li>The Record block object</li>
14409      * <li>The "arg" argument from the load function</li>
14410      * <li>A boolean success indicator</li>
14411      * </ul>
14412      * @param {Object} scope The scope in which to call the callback
14413      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14414      */
14415     load : function(params, reader, callback, scope, arg){
14416         if(this.fireEvent("beforeload", this, params) !== false){
14417             var  o = {
14418                 params : params || {},
14419                 request: {
14420                     callback : callback,
14421                     scope : scope,
14422                     arg : arg
14423                 },
14424                 reader: reader,
14425                 callback : this.loadResponse,
14426                 scope: this
14427             };
14428             if(this.useAjax){
14429                 Roo.applyIf(o, this.conn);
14430                 if(this.activeRequest){
14431                     Roo.Ajax.abort(this.activeRequest);
14432                 }
14433                 this.activeRequest = Roo.Ajax.request(o);
14434             }else{
14435                 this.conn.request(o);
14436             }
14437         }else{
14438             callback.call(scope||this, null, arg, false);
14439         }
14440     },
14441
14442     // private
14443     loadResponse : function(o, success, response){
14444         delete this.activeRequest;
14445         if(!success){
14446             this.fireEvent("loadexception", this, o, response);
14447             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14448             return;
14449         }
14450         var result;
14451         try {
14452             result = o.reader.read(response);
14453         }catch(e){
14454             this.fireEvent("loadexception", this, o, response, e);
14455             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14456             return;
14457         }
14458         
14459         this.fireEvent("load", this, o, o.request.arg);
14460         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14461     },
14462
14463     // private
14464     update : function(dataSet){
14465
14466     },
14467
14468     // private
14469     updateResponse : function(dataSet){
14470
14471     }
14472 });/*
14473  * Based on:
14474  * Ext JS Library 1.1.1
14475  * Copyright(c) 2006-2007, Ext JS, LLC.
14476  *
14477  * Originally Released Under LGPL - original licence link has changed is not relivant.
14478  *
14479  * Fork - LGPL
14480  * <script type="text/javascript">
14481  */
14482
14483 /**
14484  * @class Roo.data.ScriptTagProxy
14485  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14486  * other than the originating domain of the running page.<br><br>
14487  * <p>
14488  * <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
14489  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14490  * <p>
14491  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14492  * source code that is used as the source inside a &lt;script> tag.<br><br>
14493  * <p>
14494  * In order for the browser to process the returned data, the server must wrap the data object
14495  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14496  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14497  * depending on whether the callback name was passed:
14498  * <p>
14499  * <pre><code>
14500 boolean scriptTag = false;
14501 String cb = request.getParameter("callback");
14502 if (cb != null) {
14503     scriptTag = true;
14504     response.setContentType("text/javascript");
14505 } else {
14506     response.setContentType("application/x-json");
14507 }
14508 Writer out = response.getWriter();
14509 if (scriptTag) {
14510     out.write(cb + "(");
14511 }
14512 out.print(dataBlock.toJsonString());
14513 if (scriptTag) {
14514     out.write(");");
14515 }
14516 </pre></code>
14517  *
14518  * @constructor
14519  * @param {Object} config A configuration object.
14520  */
14521 Roo.data.ScriptTagProxy = function(config){
14522     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14523     Roo.apply(this, config);
14524     this.head = document.getElementsByTagName("head")[0];
14525 };
14526
14527 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14528
14529 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14530     /**
14531      * @cfg {String} url The URL from which to request the data object.
14532      */
14533     /**
14534      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14535      */
14536     timeout : 30000,
14537     /**
14538      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14539      * the server the name of the callback function set up by the load call to process the returned data object.
14540      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14541      * javascript output which calls this named function passing the data object as its only parameter.
14542      */
14543     callbackParam : "callback",
14544     /**
14545      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14546      * name to the request.
14547      */
14548     nocache : true,
14549
14550     /**
14551      * Load data from the configured URL, read the data object into
14552      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14553      * process that block using the passed callback.
14554      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14555      * for the request to the remote server.
14556      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14557      * object into a block of Roo.data.Records.
14558      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14559      * The function must be passed <ul>
14560      * <li>The Record block object</li>
14561      * <li>The "arg" argument from the load function</li>
14562      * <li>A boolean success indicator</li>
14563      * </ul>
14564      * @param {Object} scope The scope in which to call the callback
14565      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14566      */
14567     load : function(params, reader, callback, scope, arg){
14568         if(this.fireEvent("beforeload", this, params) !== false){
14569
14570             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14571
14572             var url = this.url;
14573             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14574             if(this.nocache){
14575                 url += "&_dc=" + (new Date().getTime());
14576             }
14577             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14578             var trans = {
14579                 id : transId,
14580                 cb : "stcCallback"+transId,
14581                 scriptId : "stcScript"+transId,
14582                 params : params,
14583                 arg : arg,
14584                 url : url,
14585                 callback : callback,
14586                 scope : scope,
14587                 reader : reader
14588             };
14589             var conn = this;
14590
14591             window[trans.cb] = function(o){
14592                 conn.handleResponse(o, trans);
14593             };
14594
14595             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14596
14597             if(this.autoAbort !== false){
14598                 this.abort();
14599             }
14600
14601             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14602
14603             var script = document.createElement("script");
14604             script.setAttribute("src", url);
14605             script.setAttribute("type", "text/javascript");
14606             script.setAttribute("id", trans.scriptId);
14607             this.head.appendChild(script);
14608
14609             this.trans = trans;
14610         }else{
14611             callback.call(scope||this, null, arg, false);
14612         }
14613     },
14614
14615     // private
14616     isLoading : function(){
14617         return this.trans ? true : false;
14618     },
14619
14620     /**
14621      * Abort the current server request.
14622      */
14623     abort : function(){
14624         if(this.isLoading()){
14625             this.destroyTrans(this.trans);
14626         }
14627     },
14628
14629     // private
14630     destroyTrans : function(trans, isLoaded){
14631         this.head.removeChild(document.getElementById(trans.scriptId));
14632         clearTimeout(trans.timeoutId);
14633         if(isLoaded){
14634             window[trans.cb] = undefined;
14635             try{
14636                 delete window[trans.cb];
14637             }catch(e){}
14638         }else{
14639             // if hasn't been loaded, wait for load to remove it to prevent script error
14640             window[trans.cb] = function(){
14641                 window[trans.cb] = undefined;
14642                 try{
14643                     delete window[trans.cb];
14644                 }catch(e){}
14645             };
14646         }
14647     },
14648
14649     // private
14650     handleResponse : function(o, trans){
14651         this.trans = false;
14652         this.destroyTrans(trans, true);
14653         var result;
14654         try {
14655             result = trans.reader.readRecords(o);
14656         }catch(e){
14657             this.fireEvent("loadexception", this, o, trans.arg, e);
14658             trans.callback.call(trans.scope||window, null, trans.arg, false);
14659             return;
14660         }
14661         this.fireEvent("load", this, o, trans.arg);
14662         trans.callback.call(trans.scope||window, result, trans.arg, true);
14663     },
14664
14665     // private
14666     handleFailure : function(trans){
14667         this.trans = false;
14668         this.destroyTrans(trans, false);
14669         this.fireEvent("loadexception", this, null, trans.arg);
14670         trans.callback.call(trans.scope||window, null, trans.arg, false);
14671     }
14672 });/*
14673  * Based on:
14674  * Ext JS Library 1.1.1
14675  * Copyright(c) 2006-2007, Ext JS, LLC.
14676  *
14677  * Originally Released Under LGPL - original licence link has changed is not relivant.
14678  *
14679  * Fork - LGPL
14680  * <script type="text/javascript">
14681  */
14682
14683 /**
14684  * @class Roo.data.JsonReader
14685  * @extends Roo.data.DataReader
14686  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14687  * based on mappings in a provided Roo.data.Record constructor.
14688  * 
14689  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14690  * in the reply previously. 
14691  * 
14692  * <p>
14693  * Example code:
14694  * <pre><code>
14695 var RecordDef = Roo.data.Record.create([
14696     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14697     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14698 ]);
14699 var myReader = new Roo.data.JsonReader({
14700     totalProperty: "results",    // The property which contains the total dataset size (optional)
14701     root: "rows",                // The property which contains an Array of row objects
14702     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14703 }, RecordDef);
14704 </code></pre>
14705  * <p>
14706  * This would consume a JSON file like this:
14707  * <pre><code>
14708 { 'results': 2, 'rows': [
14709     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14710     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14711 }
14712 </code></pre>
14713  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14714  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14715  * paged from the remote server.
14716  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14717  * @cfg {String} root name of the property which contains the Array of row objects.
14718  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14719  * @cfg {Array} fields Array of field definition objects
14720  * @constructor
14721  * Create a new JsonReader
14722  * @param {Object} meta Metadata configuration options
14723  * @param {Object} recordType Either an Array of field definition objects,
14724  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14725  */
14726 Roo.data.JsonReader = function(meta, recordType){
14727     
14728     meta = meta || {};
14729     // set some defaults:
14730     Roo.applyIf(meta, {
14731         totalProperty: 'total',
14732         successProperty : 'success',
14733         root : 'data',
14734         id : 'id'
14735     });
14736     
14737     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14738 };
14739 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14740     
14741     readerType : 'Json',
14742     
14743     /**
14744      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14745      * Used by Store query builder to append _requestMeta to params.
14746      * 
14747      */
14748     metaFromRemote : false,
14749     /**
14750      * This method is only used by a DataProxy which has retrieved data from a remote server.
14751      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14752      * @return {Object} data A data block which is used by an Roo.data.Store object as
14753      * a cache of Roo.data.Records.
14754      */
14755     read : function(response){
14756         var json = response.responseText;
14757        
14758         var o = /* eval:var:o */ eval("("+json+")");
14759         if(!o) {
14760             throw {message: "JsonReader.read: Json object not found"};
14761         }
14762         
14763         if(o.metaData){
14764             
14765             delete this.ef;
14766             this.metaFromRemote = true;
14767             this.meta = o.metaData;
14768             this.recordType = Roo.data.Record.create(o.metaData.fields);
14769             this.onMetaChange(this.meta, this.recordType, o);
14770         }
14771         return this.readRecords(o);
14772     },
14773
14774     // private function a store will implement
14775     onMetaChange : function(meta, recordType, o){
14776
14777     },
14778
14779     /**
14780          * @ignore
14781          */
14782     simpleAccess: function(obj, subsc) {
14783         return obj[subsc];
14784     },
14785
14786         /**
14787          * @ignore
14788          */
14789     getJsonAccessor: function(){
14790         var re = /[\[\.]/;
14791         return function(expr) {
14792             try {
14793                 return(re.test(expr))
14794                     ? new Function("obj", "return obj." + expr)
14795                     : function(obj){
14796                         return obj[expr];
14797                     };
14798             } catch(e){}
14799             return Roo.emptyFn;
14800         };
14801     }(),
14802
14803     /**
14804      * Create a data block containing Roo.data.Records from an XML document.
14805      * @param {Object} o An object which contains an Array of row objects in the property specified
14806      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14807      * which contains the total size of the dataset.
14808      * @return {Object} data A data block which is used by an Roo.data.Store object as
14809      * a cache of Roo.data.Records.
14810      */
14811     readRecords : function(o){
14812         /**
14813          * After any data loads, the raw JSON data is available for further custom processing.
14814          * @type Object
14815          */
14816         this.o = o;
14817         var s = this.meta, Record = this.recordType,
14818             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14819
14820 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14821         if (!this.ef) {
14822             if(s.totalProperty) {
14823                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14824                 }
14825                 if(s.successProperty) {
14826                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14827                 }
14828                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14829                 if (s.id) {
14830                         var g = this.getJsonAccessor(s.id);
14831                         this.getId = function(rec) {
14832                                 var r = g(rec);  
14833                                 return (r === undefined || r === "") ? null : r;
14834                         };
14835                 } else {
14836                         this.getId = function(){return null;};
14837                 }
14838             this.ef = [];
14839             for(var jj = 0; jj < fl; jj++){
14840                 f = fi[jj];
14841                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14842                 this.ef[jj] = this.getJsonAccessor(map);
14843             }
14844         }
14845
14846         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14847         if(s.totalProperty){
14848             var vt = parseInt(this.getTotal(o), 10);
14849             if(!isNaN(vt)){
14850                 totalRecords = vt;
14851             }
14852         }
14853         if(s.successProperty){
14854             var vs = this.getSuccess(o);
14855             if(vs === false || vs === 'false'){
14856                 success = false;
14857             }
14858         }
14859         var records = [];
14860         for(var i = 0; i < c; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = this.getId(n);
14864             for(var j = 0; j < fl; j++){
14865                 f = fi[j];
14866             var v = this.ef[j](n);
14867             if (!f.convert) {
14868                 Roo.log('missing convert for ' + f.name);
14869                 Roo.log(f);
14870                 continue;
14871             }
14872             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14873             }
14874             var record = new Record(values, id);
14875             record.json = n;
14876             records[i] = record;
14877         }
14878         return {
14879             raw : o,
14880             success : success,
14881             records : records,
14882             totalRecords : totalRecords
14883         };
14884     },
14885     // used when loading children.. @see loadDataFromChildren
14886     toLoadData: function(rec)
14887     {
14888         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14889         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14890         return { data : data, total : data.length };
14891         
14892     }
14893 });/*
14894  * Based on:
14895  * Ext JS Library 1.1.1
14896  * Copyright(c) 2006-2007, Ext JS, LLC.
14897  *
14898  * Originally Released Under LGPL - original licence link has changed is not relivant.
14899  *
14900  * Fork - LGPL
14901  * <script type="text/javascript">
14902  */
14903
14904 /**
14905  * @class Roo.data.ArrayReader
14906  * @extends Roo.data.DataReader
14907  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14908  * Each element of that Array represents a row of data fields. The
14909  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14910  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14911  * <p>
14912  * Example code:.
14913  * <pre><code>
14914 var RecordDef = Roo.data.Record.create([
14915     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14916     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14917 ]);
14918 var myReader = new Roo.data.ArrayReader({
14919     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14920 }, RecordDef);
14921 </code></pre>
14922  * <p>
14923  * This would consume an Array like this:
14924  * <pre><code>
14925 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14926   </code></pre>
14927  
14928  * @constructor
14929  * Create a new JsonReader
14930  * @param {Object} meta Metadata configuration options.
14931  * @param {Object|Array} recordType Either an Array of field definition objects
14932  * 
14933  * @cfg {Array} fields Array of field definition objects
14934  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14935  * as specified to {@link Roo.data.Record#create},
14936  * or an {@link Roo.data.Record} object
14937  *
14938  * 
14939  * created using {@link Roo.data.Record#create}.
14940  */
14941 Roo.data.ArrayReader = function(meta, recordType)
14942 {    
14943     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14944 };
14945
14946 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14947     
14948       /**
14949      * Create a data block containing Roo.data.Records from an XML document.
14950      * @param {Object} o An Array of row objects which represents the dataset.
14951      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14952      * a cache of Roo.data.Records.
14953      */
14954     readRecords : function(o)
14955     {
14956         var sid = this.meta ? this.meta.id : null;
14957         var recordType = this.recordType, fields = recordType.prototype.fields;
14958         var records = [];
14959         var root = o;
14960         for(var i = 0; i < root.length; i++){
14961                 var n = root[i];
14962             var values = {};
14963             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14964             for(var j = 0, jlen = fields.length; j < jlen; j++){
14965                 var f = fields.items[j];
14966                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14967                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14968                 v = f.convert(v);
14969                 values[f.name] = v;
14970             }
14971             var record = new recordType(values, id);
14972             record.json = n;
14973             records[records.length] = record;
14974         }
14975         return {
14976             records : records,
14977             totalRecords : records.length
14978         };
14979     },
14980     // used when loading children.. @see loadDataFromChildren
14981     toLoadData: function(rec)
14982     {
14983         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14984         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14985         
14986     }
14987     
14988     
14989 });/*
14990  * - LGPL
14991  * * 
14992  */
14993
14994 /**
14995  * @class Roo.bootstrap.ComboBox
14996  * @extends Roo.bootstrap.TriggerField
14997  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14998  * @cfg {Boolean} append (true|false) default false
14999  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15000  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15001  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15002  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15003  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15004  * @cfg {Boolean} animate default true
15005  * @cfg {Boolean} emptyResultText only for touch device
15006  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15007  * @cfg {String} emptyTitle default ''
15008  * @cfg {Number} width fixed with? experimental
15009  * @constructor
15010  * Create a new ComboBox.
15011  * @param {Object} config Configuration options
15012  */
15013 Roo.bootstrap.ComboBox = function(config){
15014     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15015     this.addEvents({
15016         /**
15017          * @event expand
15018          * Fires when the dropdown list is expanded
15019         * @param {Roo.bootstrap.ComboBox} combo This combo box
15020         */
15021         'expand' : true,
15022         /**
15023          * @event collapse
15024          * Fires when the dropdown list is collapsed
15025         * @param {Roo.bootstrap.ComboBox} combo This combo box
15026         */
15027         'collapse' : true,
15028         /**
15029          * @event beforeselect
15030          * Fires before a list item is selected. Return false to cancel the selection.
15031         * @param {Roo.bootstrap.ComboBox} combo This combo box
15032         * @param {Roo.data.Record} record The data record returned from the underlying store
15033         * @param {Number} index The index of the selected item in the dropdown list
15034         */
15035         'beforeselect' : true,
15036         /**
15037          * @event select
15038          * Fires when a list item is selected
15039         * @param {Roo.bootstrap.ComboBox} combo This combo box
15040         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15041         * @param {Number} index The index of the selected item in the dropdown list
15042         */
15043         'select' : true,
15044         /**
15045          * @event beforequery
15046          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15047          * The event object passed has these properties:
15048         * @param {Roo.bootstrap.ComboBox} combo This combo box
15049         * @param {String} query The query
15050         * @param {Boolean} forceAll true to force "all" query
15051         * @param {Boolean} cancel true to cancel the query
15052         * @param {Object} e The query event object
15053         */
15054         'beforequery': true,
15055          /**
15056          * @event add
15057          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15058         * @param {Roo.bootstrap.ComboBox} combo This combo box
15059         */
15060         'add' : true,
15061         /**
15062          * @event edit
15063          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15064         * @param {Roo.bootstrap.ComboBox} combo This combo box
15065         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15066         */
15067         'edit' : true,
15068         /**
15069          * @event remove
15070          * Fires when the remove value from the combobox array
15071         * @param {Roo.bootstrap.ComboBox} combo This combo box
15072         */
15073         'remove' : true,
15074         /**
15075          * @event afterremove
15076          * Fires when the remove value from the combobox array
15077         * @param {Roo.bootstrap.ComboBox} combo This combo box
15078         */
15079         'afterremove' : true,
15080         /**
15081          * @event specialfilter
15082          * Fires when specialfilter
15083             * @param {Roo.bootstrap.ComboBox} combo This combo box
15084             */
15085         'specialfilter' : true,
15086         /**
15087          * @event tick
15088          * Fires when tick the element
15089             * @param {Roo.bootstrap.ComboBox} combo This combo box
15090             */
15091         'tick' : true,
15092         /**
15093          * @event touchviewdisplay
15094          * Fires when touch view require special display (default is using displayField)
15095             * @param {Roo.bootstrap.ComboBox} combo This combo box
15096             * @param {Object} cfg set html .
15097             */
15098         'touchviewdisplay' : true
15099         
15100     });
15101     
15102     this.item = [];
15103     this.tickItems = [];
15104     
15105     this.selectedIndex = -1;
15106     if(this.mode == 'local'){
15107         if(config.queryDelay === undefined){
15108             this.queryDelay = 10;
15109         }
15110         if(config.minChars === undefined){
15111             this.minChars = 0;
15112         }
15113     }
15114 };
15115
15116 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15117      
15118     /**
15119      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15120      * rendering into an Roo.Editor, defaults to false)
15121      */
15122     /**
15123      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15124      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15125      */
15126     /**
15127      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15128      */
15129     /**
15130      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15131      * the dropdown list (defaults to undefined, with no header element)
15132      */
15133
15134      /**
15135      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15136      */
15137      
15138      /**
15139      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15140      */
15141     listWidth: undefined,
15142     /**
15143      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15144      * mode = 'remote' or 'text' if mode = 'local')
15145      */
15146     displayField: undefined,
15147     
15148     /**
15149      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15150      * mode = 'remote' or 'value' if mode = 'local'). 
15151      * Note: use of a valueField requires the user make a selection
15152      * in order for a value to be mapped.
15153      */
15154     valueField: undefined,
15155     /**
15156      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15157      */
15158     modalTitle : '',
15159     
15160     /**
15161      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15162      * field's data value (defaults to the underlying DOM element's name)
15163      */
15164     hiddenName: undefined,
15165     /**
15166      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15167      */
15168     listClass: '',
15169     /**
15170      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15171      */
15172     selectedClass: 'active',
15173     
15174     /**
15175      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15176      */
15177     shadow:'sides',
15178     /**
15179      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15180      * anchor positions (defaults to 'tl-bl')
15181      */
15182     listAlign: 'tl-bl?',
15183     /**
15184      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15185      */
15186     maxHeight: 300,
15187     /**
15188      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15189      * query specified by the allQuery config option (defaults to 'query')
15190      */
15191     triggerAction: 'query',
15192     /**
15193      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15194      * (defaults to 4, does not apply if editable = false)
15195      */
15196     minChars : 4,
15197     /**
15198      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15199      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15200      */
15201     typeAhead: false,
15202     /**
15203      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15204      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15205      */
15206     queryDelay: 500,
15207     /**
15208      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15209      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15210      */
15211     pageSize: 0,
15212     /**
15213      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15214      * when editable = true (defaults to false)
15215      */
15216     selectOnFocus:false,
15217     /**
15218      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15219      */
15220     queryParam: 'query',
15221     /**
15222      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15223      * when mode = 'remote' (defaults to 'Loading...')
15224      */
15225     loadingText: 'Loading...',
15226     /**
15227      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15228      */
15229     resizable: false,
15230     /**
15231      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15232      */
15233     handleHeight : 8,
15234     /**
15235      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15236      * traditional select (defaults to true)
15237      */
15238     editable: true,
15239     /**
15240      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15241      */
15242     allQuery: '',
15243     /**
15244      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15245      */
15246     mode: 'remote',
15247     /**
15248      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15249      * listWidth has a higher value)
15250      */
15251     minListWidth : 70,
15252     /**
15253      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15254      * allow the user to set arbitrary text into the field (defaults to false)
15255      */
15256     forceSelection:false,
15257     /**
15258      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15259      * if typeAhead = true (defaults to 250)
15260      */
15261     typeAheadDelay : 250,
15262     /**
15263      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15264      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15265      */
15266     valueNotFoundText : undefined,
15267     /**
15268      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15269      */
15270     blockFocus : false,
15271     
15272     /**
15273      * @cfg {Boolean} disableClear Disable showing of clear button.
15274      */
15275     disableClear : false,
15276     /**
15277      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15278      */
15279     alwaysQuery : false,
15280     
15281     /**
15282      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15283      */
15284     multiple : false,
15285     
15286     /**
15287      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15288      */
15289     invalidClass : "has-warning",
15290     
15291     /**
15292      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15293      */
15294     validClass : "has-success",
15295     
15296     /**
15297      * @cfg {Boolean} specialFilter (true|false) special filter default false
15298      */
15299     specialFilter : false,
15300     
15301     /**
15302      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15303      */
15304     mobileTouchView : true,
15305     
15306     /**
15307      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15308      */
15309     useNativeIOS : false,
15310     
15311     /**
15312      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15313      */
15314     mobile_restrict_height : false,
15315     
15316     ios_options : false,
15317     
15318     //private
15319     addicon : false,
15320     editicon: false,
15321     
15322     page: 0,
15323     hasQuery: false,
15324     append: false,
15325     loadNext: false,
15326     autoFocus : true,
15327     tickable : false,
15328     btnPosition : 'right',
15329     triggerList : true,
15330     showToggleBtn : true,
15331     animate : true,
15332     emptyResultText: 'Empty',
15333     triggerText : 'Select',
15334     emptyTitle : '',
15335     width : false,
15336     
15337     // element that contains real text value.. (when hidden is used..)
15338     
15339     getAutoCreate : function()
15340     {   
15341         var cfg = false;
15342         //render
15343         /*
15344          * Render classic select for iso
15345          */
15346         
15347         if(Roo.isIOS && this.useNativeIOS){
15348             cfg = this.getAutoCreateNativeIOS();
15349             return cfg;
15350         }
15351         
15352         /*
15353          * Touch Devices
15354          */
15355         
15356         if(Roo.isTouch && this.mobileTouchView){
15357             cfg = this.getAutoCreateTouchView();
15358             return cfg;;
15359         }
15360         
15361         /*
15362          *  Normal ComboBox
15363          */
15364         if(!this.tickable){
15365             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15366             return cfg;
15367         }
15368         
15369         /*
15370          *  ComboBox with tickable selections
15371          */
15372              
15373         var align = this.labelAlign || this.parentLabelAlign();
15374         
15375         cfg = {
15376             cls : 'form-group roo-combobox-tickable' //input-group
15377         };
15378         
15379         var btn_text_select = '';
15380         var btn_text_done = '';
15381         var btn_text_cancel = '';
15382         
15383         if (this.btn_text_show) {
15384             btn_text_select = 'Select';
15385             btn_text_done = 'Done';
15386             btn_text_cancel = 'Cancel'; 
15387         }
15388         
15389         var buttons = {
15390             tag : 'div',
15391             cls : 'tickable-buttons',
15392             cn : [
15393                 {
15394                     tag : 'button',
15395                     type : 'button',
15396                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15397                     //html : this.triggerText
15398                     html: btn_text_select
15399                 },
15400                 {
15401                     tag : 'button',
15402                     type : 'button',
15403                     name : 'ok',
15404                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15405                     //html : 'Done'
15406                     html: btn_text_done
15407                 },
15408                 {
15409                     tag : 'button',
15410                     type : 'button',
15411                     name : 'cancel',
15412                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15413                     //html : 'Cancel'
15414                     html: btn_text_cancel
15415                 }
15416             ]
15417         };
15418         
15419         if(this.editable){
15420             buttons.cn.unshift({
15421                 tag: 'input',
15422                 cls: 'roo-select2-search-field-input'
15423             });
15424         }
15425         
15426         var _this = this;
15427         
15428         Roo.each(buttons.cn, function(c){
15429             if (_this.size) {
15430                 c.cls += ' btn-' + _this.size;
15431             }
15432
15433             if (_this.disabled) {
15434                 c.disabled = true;
15435             }
15436         });
15437         
15438         var box = {
15439             tag: 'div',
15440             style : 'display: contents',
15441             cn: [
15442                 {
15443                     tag: 'input',
15444                     type : 'hidden',
15445                     cls: 'form-hidden-field'
15446                 },
15447                 {
15448                     tag: 'ul',
15449                     cls: 'roo-select2-choices',
15450                     cn:[
15451                         {
15452                             tag: 'li',
15453                             cls: 'roo-select2-search-field',
15454                             cn: [
15455                                 buttons
15456                             ]
15457                         }
15458                     ]
15459                 }
15460             ]
15461         };
15462         
15463         var combobox = {
15464             cls: 'roo-select2-container input-group roo-select2-container-multi',
15465             cn: [
15466                 
15467                 box
15468 //                {
15469 //                    tag: 'ul',
15470 //                    cls: 'typeahead typeahead-long dropdown-menu',
15471 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15472 //                }
15473             ]
15474         };
15475         
15476         if(this.hasFeedback && !this.allowBlank){
15477             
15478             var feedback = {
15479                 tag: 'span',
15480                 cls: 'glyphicon form-control-feedback'
15481             };
15482
15483             combobox.cn.push(feedback);
15484         }
15485         
15486         
15487         
15488         var indicator = {
15489             tag : 'i',
15490             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15491             tooltip : 'This field is required'
15492         };
15493         if (Roo.bootstrap.version == 4) {
15494             indicator = {
15495                 tag : 'i',
15496                 style : 'display:none'
15497             };
15498         }
15499         if (align ==='left' && this.fieldLabel.length) {
15500             
15501             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15502             
15503             cfg.cn = [
15504                 indicator,
15505                 {
15506                     tag: 'label',
15507                     'for' :  id,
15508                     cls : 'control-label col-form-label',
15509                     html : this.fieldLabel
15510
15511                 },
15512                 {
15513                     cls : "", 
15514                     cn: [
15515                         combobox
15516                     ]
15517                 }
15518
15519             ];
15520             
15521             var labelCfg = cfg.cn[1];
15522             var contentCfg = cfg.cn[2];
15523             
15524
15525             if(this.indicatorpos == 'right'){
15526                 
15527                 cfg.cn = [
15528                     {
15529                         tag: 'label',
15530                         'for' :  id,
15531                         cls : 'control-label col-form-label',
15532                         cn : [
15533                             {
15534                                 tag : 'span',
15535                                 html : this.fieldLabel
15536                             },
15537                             indicator
15538                         ]
15539                     },
15540                     {
15541                         cls : "",
15542                         cn: [
15543                             combobox
15544                         ]
15545                     }
15546
15547                 ];
15548                 
15549                 
15550                 
15551                 labelCfg = cfg.cn[0];
15552                 contentCfg = cfg.cn[1];
15553             
15554             }
15555             
15556             if(this.labelWidth > 12){
15557                 labelCfg.style = "width: " + this.labelWidth + 'px';
15558             }
15559             if(this.width * 1 > 0){
15560                 contentCfg.style = "width: " + this.width + 'px';
15561             }
15562             if(this.labelWidth < 13 && this.labelmd == 0){
15563                 this.labelmd = this.labelWidth;
15564             }
15565             
15566             if(this.labellg > 0){
15567                 labelCfg.cls += ' col-lg-' + this.labellg;
15568                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15569             }
15570             
15571             if(this.labelmd > 0){
15572                 labelCfg.cls += ' col-md-' + this.labelmd;
15573                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15574             }
15575             
15576             if(this.labelsm > 0){
15577                 labelCfg.cls += ' col-sm-' + this.labelsm;
15578                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15579             }
15580             
15581             if(this.labelxs > 0){
15582                 labelCfg.cls += ' col-xs-' + this.labelxs;
15583                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15584             }
15585                 
15586                 
15587         } else if ( this.fieldLabel.length) {
15588 //                Roo.log(" label");
15589                  cfg.cn = [
15590                    indicator,
15591                     {
15592                         tag: 'label',
15593                         //cls : 'input-group-addon',
15594                         html : this.fieldLabel
15595                     },
15596                     combobox
15597                 ];
15598                 
15599                 if(this.indicatorpos == 'right'){
15600                     cfg.cn = [
15601                         {
15602                             tag: 'label',
15603                             //cls : 'input-group-addon',
15604                             html : this.fieldLabel
15605                         },
15606                         indicator,
15607                         combobox
15608                     ];
15609                     
15610                 }
15611
15612         } else {
15613             
15614 //                Roo.log(" no label && no align");
15615                 cfg = combobox
15616                      
15617                 
15618         }
15619          
15620         var settings=this;
15621         ['xs','sm','md','lg'].map(function(size){
15622             if (settings[size]) {
15623                 cfg.cls += ' col-' + size + '-' + settings[size];
15624             }
15625         });
15626         
15627         return cfg;
15628         
15629     },
15630     
15631     _initEventsCalled : false,
15632     
15633     // private
15634     initEvents: function()
15635     {   
15636         if (this._initEventsCalled) { // as we call render... prevent looping...
15637             return;
15638         }
15639         this._initEventsCalled = true;
15640         
15641         if (!this.store) {
15642             throw "can not find store for combo";
15643         }
15644         
15645         this.indicator = this.indicatorEl();
15646         
15647         this.store = Roo.factory(this.store, Roo.data);
15648         this.store.parent = this;
15649         
15650         // if we are building from html. then this element is so complex, that we can not really
15651         // use the rendered HTML.
15652         // so we have to trash and replace the previous code.
15653         if (Roo.XComponent.build_from_html) {
15654             // remove this element....
15655             var e = this.el.dom, k=0;
15656             while (e ) { e = e.previousSibling;  ++k;}
15657
15658             this.el.remove();
15659             
15660             this.el=false;
15661             this.rendered = false;
15662             
15663             this.render(this.parent().getChildContainer(true), k);
15664         }
15665         
15666         if(Roo.isIOS && this.useNativeIOS){
15667             this.initIOSView();
15668             return;
15669         }
15670         
15671         /*
15672          * Touch Devices
15673          */
15674         
15675         if(Roo.isTouch && this.mobileTouchView){
15676             this.initTouchView();
15677             return;
15678         }
15679         
15680         if(this.tickable){
15681             this.initTickableEvents();
15682             return;
15683         }
15684         
15685         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15686         
15687         if(this.hiddenName){
15688             
15689             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15690             
15691             this.hiddenField.dom.value =
15692                 this.hiddenValue !== undefined ? this.hiddenValue :
15693                 this.value !== undefined ? this.value : '';
15694
15695             // prevent input submission
15696             this.el.dom.removeAttribute('name');
15697             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15698              
15699              
15700         }
15701         //if(Roo.isGecko){
15702         //    this.el.dom.setAttribute('autocomplete', 'off');
15703         //}
15704         
15705         var cls = 'x-combo-list';
15706         
15707         //this.list = new Roo.Layer({
15708         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15709         //});
15710         
15711         var _this = this;
15712         
15713         (function(){
15714             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15715             _this.list.setWidth(lw);
15716         }).defer(100);
15717         
15718         this.list.on('mouseover', this.onViewOver, this);
15719         this.list.on('mousemove', this.onViewMove, this);
15720         this.list.on('scroll', this.onViewScroll, this);
15721         
15722         /*
15723         this.list.swallowEvent('mousewheel');
15724         this.assetHeight = 0;
15725
15726         if(this.title){
15727             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15728             this.assetHeight += this.header.getHeight();
15729         }
15730
15731         this.innerList = this.list.createChild({cls:cls+'-inner'});
15732         this.innerList.on('mouseover', this.onViewOver, this);
15733         this.innerList.on('mousemove', this.onViewMove, this);
15734         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15735         
15736         if(this.allowBlank && !this.pageSize && !this.disableClear){
15737             this.footer = this.list.createChild({cls:cls+'-ft'});
15738             this.pageTb = new Roo.Toolbar(this.footer);
15739            
15740         }
15741         if(this.pageSize){
15742             this.footer = this.list.createChild({cls:cls+'-ft'});
15743             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15744                     {pageSize: this.pageSize});
15745             
15746         }
15747         
15748         if (this.pageTb && this.allowBlank && !this.disableClear) {
15749             var _this = this;
15750             this.pageTb.add(new Roo.Toolbar.Fill(), {
15751                 cls: 'x-btn-icon x-btn-clear',
15752                 text: '&#160;',
15753                 handler: function()
15754                 {
15755                     _this.collapse();
15756                     _this.clearValue();
15757                     _this.onSelect(false, -1);
15758                 }
15759             });
15760         }
15761         if (this.footer) {
15762             this.assetHeight += this.footer.getHeight();
15763         }
15764         */
15765             
15766         if(!this.tpl){
15767             this.tpl = Roo.bootstrap.version == 4 ?
15768                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15769                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15770         }
15771
15772         this.view = new Roo.View(this.list, this.tpl, {
15773             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15774         });
15775         //this.view.wrapEl.setDisplayed(false);
15776         this.view.on('click', this.onViewClick, this);
15777         
15778         
15779         this.store.on('beforeload', this.onBeforeLoad, this);
15780         this.store.on('load', this.onLoad, this);
15781         this.store.on('loadexception', this.onLoadException, this);
15782         /*
15783         if(this.resizable){
15784             this.resizer = new Roo.Resizable(this.list,  {
15785                pinned:true, handles:'se'
15786             });
15787             this.resizer.on('resize', function(r, w, h){
15788                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15789                 this.listWidth = w;
15790                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15791                 this.restrictHeight();
15792             }, this);
15793             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15794         }
15795         */
15796         if(!this.editable){
15797             this.editable = true;
15798             this.setEditable(false);
15799         }
15800         
15801         /*
15802         
15803         if (typeof(this.events.add.listeners) != 'undefined') {
15804             
15805             this.addicon = this.wrap.createChild(
15806                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15807        
15808             this.addicon.on('click', function(e) {
15809                 this.fireEvent('add', this);
15810             }, this);
15811         }
15812         if (typeof(this.events.edit.listeners) != 'undefined') {
15813             
15814             this.editicon = this.wrap.createChild(
15815                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15816             if (this.addicon) {
15817                 this.editicon.setStyle('margin-left', '40px');
15818             }
15819             this.editicon.on('click', function(e) {
15820                 
15821                 // we fire even  if inothing is selected..
15822                 this.fireEvent('edit', this, this.lastData );
15823                 
15824             }, this);
15825         }
15826         */
15827         
15828         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15829             "up" : function(e){
15830                 this.inKeyMode = true;
15831                 this.selectPrev();
15832             },
15833
15834             "down" : function(e){
15835                 if(!this.isExpanded()){
15836                     this.onTriggerClick();
15837                 }else{
15838                     this.inKeyMode = true;
15839                     this.selectNext();
15840                 }
15841             },
15842
15843             "enter" : function(e){
15844 //                this.onViewClick();
15845                 //return true;
15846                 this.collapse();
15847                 
15848                 if(this.fireEvent("specialkey", this, e)){
15849                     this.onViewClick(false);
15850                 }
15851                 
15852                 return true;
15853             },
15854
15855             "esc" : function(e){
15856                 this.collapse();
15857             },
15858
15859             "tab" : function(e){
15860                 this.collapse();
15861                 
15862                 if(this.fireEvent("specialkey", this, e)){
15863                     this.onViewClick(false);
15864                 }
15865                 
15866                 return true;
15867             },
15868
15869             scope : this,
15870
15871             doRelay : function(foo, bar, hname){
15872                 if(hname == 'down' || this.scope.isExpanded()){
15873                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15874                 }
15875                 return true;
15876             },
15877
15878             forceKeyDown: true
15879         });
15880         
15881         
15882         this.queryDelay = Math.max(this.queryDelay || 10,
15883                 this.mode == 'local' ? 10 : 250);
15884         
15885         
15886         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15887         
15888         if(this.typeAhead){
15889             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15890         }
15891         if(this.editable !== false){
15892             this.inputEl().on("keyup", this.onKeyUp, this);
15893         }
15894         if(this.forceSelection){
15895             this.inputEl().on('blur', this.doForce, this);
15896         }
15897         
15898         if(this.multiple){
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         }
15902     },
15903     
15904     initTickableEvents: function()
15905     {   
15906         this.createList();
15907         
15908         if(this.hiddenName){
15909             
15910             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15911             
15912             this.hiddenField.dom.value =
15913                 this.hiddenValue !== undefined ? this.hiddenValue :
15914                 this.value !== undefined ? this.value : '';
15915
15916             // prevent input submission
15917             this.el.dom.removeAttribute('name');
15918             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15919              
15920              
15921         }
15922         
15923 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15924         
15925         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15926         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15927         if(this.triggerList){
15928             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15929         }
15930          
15931         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15932         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15933         
15934         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15935         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15936         
15937         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15938         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15939         
15940         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15941         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15942         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15943         
15944         this.okBtn.hide();
15945         this.cancelBtn.hide();
15946         
15947         var _this = this;
15948         
15949         (function(){
15950             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15951             _this.list.setWidth(lw);
15952         }).defer(100);
15953         
15954         this.list.on('mouseover', this.onViewOver, this);
15955         this.list.on('mousemove', this.onViewMove, this);
15956         
15957         this.list.on('scroll', this.onViewScroll, this);
15958         
15959         if(!this.tpl){
15960             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15961                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15962         }
15963
15964         this.view = new Roo.View(this.list, this.tpl, {
15965             singleSelect:true,
15966             tickable:true,
15967             parent:this,
15968             store: this.store,
15969             selectedClass: this.selectedClass
15970         });
15971         
15972         //this.view.wrapEl.setDisplayed(false);
15973         this.view.on('click', this.onViewClick, this);
15974         
15975         
15976         
15977         this.store.on('beforeload', this.onBeforeLoad, this);
15978         this.store.on('load', this.onLoad, this);
15979         this.store.on('loadexception', this.onLoadException, this);
15980         
15981         if(this.editable){
15982             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15983                 "up" : function(e){
15984                     this.inKeyMode = true;
15985                     this.selectPrev();
15986                 },
15987
15988                 "down" : function(e){
15989                     this.inKeyMode = true;
15990                     this.selectNext();
15991                 },
15992
15993                 "enter" : function(e){
15994                     if(this.fireEvent("specialkey", this, e)){
15995                         this.onViewClick(false);
15996                     }
15997                     
15998                     return true;
15999                 },
16000
16001                 "esc" : function(e){
16002                     this.onTickableFooterButtonClick(e, false, false);
16003                 },
16004
16005                 "tab" : function(e){
16006                     this.fireEvent("specialkey", this, e);
16007                     
16008                     this.onTickableFooterButtonClick(e, false, false);
16009                     
16010                     return true;
16011                 },
16012
16013                 scope : this,
16014
16015                 doRelay : function(e, fn, key){
16016                     if(this.scope.isExpanded()){
16017                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16018                     }
16019                     return true;
16020                 },
16021
16022                 forceKeyDown: true
16023             });
16024         }
16025         
16026         this.queryDelay = Math.max(this.queryDelay || 10,
16027                 this.mode == 'local' ? 10 : 250);
16028         
16029         
16030         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16031         
16032         if(this.typeAhead){
16033             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16034         }
16035         
16036         if(this.editable !== false){
16037             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16038         }
16039         
16040         this.indicator = this.indicatorEl();
16041         
16042         if(this.indicator){
16043             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16044             this.indicator.hide();
16045         }
16046         
16047     },
16048
16049     onDestroy : function(){
16050         if(this.view){
16051             this.view.setStore(null);
16052             this.view.el.removeAllListeners();
16053             this.view.el.remove();
16054             this.view.purgeListeners();
16055         }
16056         if(this.list){
16057             this.list.dom.innerHTML  = '';
16058         }
16059         
16060         if(this.store){
16061             this.store.un('beforeload', this.onBeforeLoad, this);
16062             this.store.un('load', this.onLoad, this);
16063             this.store.un('loadexception', this.onLoadException, this);
16064         }
16065         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16066     },
16067
16068     // private
16069     fireKey : function(e){
16070         if(e.isNavKeyPress() && !this.list.isVisible()){
16071             this.fireEvent("specialkey", this, e);
16072         }
16073     },
16074
16075     // private
16076     onResize: function(w, h)
16077     {
16078         
16079         
16080 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16081 //        
16082 //        if(typeof w != 'number'){
16083 //            // we do not handle it!?!?
16084 //            return;
16085 //        }
16086 //        var tw = this.trigger.getWidth();
16087 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16088 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16089 //        var x = w - tw;
16090 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16091 //            
16092 //        //this.trigger.setStyle('left', x+'px');
16093 //        
16094 //        if(this.list && this.listWidth === undefined){
16095 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16096 //            this.list.setWidth(lw);
16097 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16098 //        }
16099         
16100     
16101         
16102     },
16103
16104     /**
16105      * Allow or prevent the user from directly editing the field text.  If false is passed,
16106      * the user will only be able to select from the items defined in the dropdown list.  This method
16107      * is the runtime equivalent of setting the 'editable' config option at config time.
16108      * @param {Boolean} value True to allow the user to directly edit the field text
16109      */
16110     setEditable : function(value){
16111         if(value == this.editable){
16112             return;
16113         }
16114         this.editable = value;
16115         if(!value){
16116             this.inputEl().dom.setAttribute('readOnly', true);
16117             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16118             this.inputEl().addClass('x-combo-noedit');
16119         }else{
16120             this.inputEl().dom.setAttribute('readOnly', false);
16121             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16122             this.inputEl().removeClass('x-combo-noedit');
16123         }
16124     },
16125
16126     // private
16127     
16128     onBeforeLoad : function(combo,opts){
16129         if(!this.hasFocus){
16130             return;
16131         }
16132          if (!opts.add) {
16133             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16134          }
16135         this.restrictHeight();
16136         this.selectedIndex = -1;
16137     },
16138
16139     // private
16140     onLoad : function(){
16141         
16142         this.hasQuery = false;
16143         
16144         if(!this.hasFocus){
16145             return;
16146         }
16147         
16148         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16149             this.loading.hide();
16150         }
16151         
16152         if(this.store.getCount() > 0){
16153             
16154             this.expand();
16155             this.restrictHeight();
16156             if(this.lastQuery == this.allQuery){
16157                 if(this.editable && !this.tickable){
16158                     this.inputEl().dom.select();
16159                 }
16160                 
16161                 if(
16162                     !this.selectByValue(this.value, true) &&
16163                     this.autoFocus && 
16164                     (
16165                         !this.store.lastOptions ||
16166                         typeof(this.store.lastOptions.add) == 'undefined' || 
16167                         this.store.lastOptions.add != true
16168                     )
16169                 ){
16170                     this.select(0, true);
16171                 }
16172             }else{
16173                 if(this.autoFocus){
16174                     this.selectNext();
16175                 }
16176                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16177                     this.taTask.delay(this.typeAheadDelay);
16178                 }
16179             }
16180         }else{
16181             this.onEmptyResults();
16182         }
16183         
16184         //this.el.focus();
16185     },
16186     // private
16187     onLoadException : function()
16188     {
16189         this.hasQuery = false;
16190         
16191         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16192             this.loading.hide();
16193         }
16194         
16195         if(this.tickable && this.editable){
16196             return;
16197         }
16198         
16199         this.collapse();
16200         // only causes errors at present
16201         //Roo.log(this.store.reader.jsonData);
16202         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16203             // fixme
16204             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16205         //}
16206         
16207         
16208     },
16209     // private
16210     onTypeAhead : function(){
16211         if(this.store.getCount() > 0){
16212             var r = this.store.getAt(0);
16213             var newValue = r.data[this.displayField];
16214             var len = newValue.length;
16215             var selStart = this.getRawValue().length;
16216             
16217             if(selStart != len){
16218                 this.setRawValue(newValue);
16219                 this.selectText(selStart, newValue.length);
16220             }
16221         }
16222     },
16223
16224     // private
16225     onSelect : function(record, index){
16226         
16227         if(this.fireEvent('beforeselect', this, record, index) !== false){
16228         
16229             this.setFromData(index > -1 ? record.data : false);
16230             
16231             this.collapse();
16232             this.fireEvent('select', this, record, index);
16233         }
16234     },
16235
16236     /**
16237      * Returns the currently selected field value or empty string if no value is set.
16238      * @return {String} value The selected value
16239      */
16240     getValue : function()
16241     {
16242         if(Roo.isIOS && this.useNativeIOS){
16243             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16244         }
16245         
16246         if(this.multiple){
16247             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16248         }
16249         
16250         if(this.valueField){
16251             return typeof this.value != 'undefined' ? this.value : '';
16252         }else{
16253             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16254         }
16255     },
16256     
16257     getRawValue : function()
16258     {
16259         if(Roo.isIOS && this.useNativeIOS){
16260             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16261         }
16262         
16263         var v = this.inputEl().getValue();
16264         
16265         return v;
16266     },
16267
16268     /**
16269      * Clears any text/value currently set in the field
16270      */
16271     clearValue : function(){
16272         
16273         if(this.hiddenField){
16274             this.hiddenField.dom.value = '';
16275         }
16276         this.value = '';
16277         this.setRawValue('');
16278         this.lastSelectionText = '';
16279         this.lastData = false;
16280         
16281         var close = this.closeTriggerEl();
16282         
16283         if(close){
16284             close.hide();
16285         }
16286         
16287         this.validate();
16288         
16289     },
16290
16291     /**
16292      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16293      * will be displayed in the field.  If the value does not match the data value of an existing item,
16294      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16295      * Otherwise the field will be blank (although the value will still be set).
16296      * @param {String} value The value to match
16297      */
16298     setValue : function(v)
16299     {
16300         if(Roo.isIOS && this.useNativeIOS){
16301             this.setIOSValue(v);
16302             return;
16303         }
16304         
16305         if(this.multiple){
16306             this.syncValue();
16307             return;
16308         }
16309         
16310         var text = v;
16311         if(this.valueField){
16312             var r = this.findRecord(this.valueField, v);
16313             if(r){
16314                 text = r.data[this.displayField];
16315             }else if(this.valueNotFoundText !== undefined){
16316                 text = this.valueNotFoundText;
16317             }
16318         }
16319         this.lastSelectionText = text;
16320         if(this.hiddenField){
16321             this.hiddenField.dom.value = v;
16322         }
16323         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16324         this.value = v;
16325         
16326         var close = this.closeTriggerEl();
16327         
16328         if(close){
16329             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16330         }
16331         
16332         this.validate();
16333     },
16334     /**
16335      * @property {Object} the last set data for the element
16336      */
16337     
16338     lastData : false,
16339     /**
16340      * Sets the value of the field based on a object which is related to the record format for the store.
16341      * @param {Object} value the value to set as. or false on reset?
16342      */
16343     setFromData : function(o){
16344         
16345         if(this.multiple){
16346             this.addItem(o);
16347             return;
16348         }
16349             
16350         var dv = ''; // display value
16351         var vv = ''; // value value..
16352         this.lastData = o;
16353         if (this.displayField) {
16354             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16355         } else {
16356             // this is an error condition!!!
16357             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16358         }
16359         
16360         if(this.valueField){
16361             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16362         }
16363         
16364         var close = this.closeTriggerEl();
16365         
16366         if(close){
16367             if(dv.length || vv * 1 > 0){
16368                 close.show() ;
16369                 this.blockFocus=true;
16370             } else {
16371                 close.hide();
16372             }             
16373         }
16374         
16375         if(this.hiddenField){
16376             this.hiddenField.dom.value = vv;
16377             
16378             this.lastSelectionText = dv;
16379             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16380             this.value = vv;
16381             return;
16382         }
16383         // no hidden field.. - we store the value in 'value', but still display
16384         // display field!!!!
16385         this.lastSelectionText = dv;
16386         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16387         this.value = vv;
16388         
16389         
16390         
16391     },
16392     // private
16393     reset : function(){
16394         // overridden so that last data is reset..
16395         
16396         if(this.multiple){
16397             this.clearItem();
16398             return;
16399         }
16400         
16401         this.setValue(this.originalValue);
16402         //this.clearInvalid();
16403         this.lastData = false;
16404         if (this.view) {
16405             this.view.clearSelections();
16406         }
16407         
16408         this.validate();
16409     },
16410     // private
16411     findRecord : function(prop, value){
16412         var record;
16413         if(this.store.getCount() > 0){
16414             this.store.each(function(r){
16415                 if(r.data[prop] == value){
16416                     record = r;
16417                     return false;
16418                 }
16419                 return true;
16420             });
16421         }
16422         return record;
16423     },
16424     
16425     getName: function()
16426     {
16427         // returns hidden if it's set..
16428         if (!this.rendered) {return ''};
16429         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16430         
16431     },
16432     // private
16433     onViewMove : function(e, t){
16434         this.inKeyMode = false;
16435     },
16436
16437     // private
16438     onViewOver : function(e, t){
16439         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16440             return;
16441         }
16442         var item = this.view.findItemFromChild(t);
16443         
16444         if(item){
16445             var index = this.view.indexOf(item);
16446             this.select(index, false);
16447         }
16448     },
16449
16450     // private
16451     onViewClick : function(view, doFocus, el, e)
16452     {
16453         var index = this.view.getSelectedIndexes()[0];
16454         
16455         var r = this.store.getAt(index);
16456         
16457         if(this.tickable){
16458             
16459             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16460                 return;
16461             }
16462             
16463             var rm = false;
16464             var _this = this;
16465             
16466             Roo.each(this.tickItems, function(v,k){
16467                 
16468                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16469                     Roo.log(v);
16470                     _this.tickItems.splice(k, 1);
16471                     
16472                     if(typeof(e) == 'undefined' && view == false){
16473                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16474                     }
16475                     
16476                     rm = true;
16477                     return;
16478                 }
16479             });
16480             
16481             if(rm){
16482                 return;
16483             }
16484             
16485             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16486                 this.tickItems.push(r.data);
16487             }
16488             
16489             if(typeof(e) == 'undefined' && view == false){
16490                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16491             }
16492                     
16493             return;
16494         }
16495         
16496         if(r){
16497             this.onSelect(r, index);
16498         }
16499         if(doFocus !== false && !this.blockFocus){
16500             this.inputEl().focus();
16501         }
16502     },
16503
16504     // private
16505     restrictHeight : function(){
16506         //this.innerList.dom.style.height = '';
16507         //var inner = this.innerList.dom;
16508         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16509         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16510         //this.list.beginUpdate();
16511         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16512         this.list.alignTo(this.inputEl(), this.listAlign);
16513         this.list.alignTo(this.inputEl(), this.listAlign);
16514         //this.list.endUpdate();
16515     },
16516
16517     // private
16518     onEmptyResults : function(){
16519         
16520         if(this.tickable && this.editable){
16521             this.hasFocus = false;
16522             this.restrictHeight();
16523             return;
16524         }
16525         
16526         this.collapse();
16527     },
16528
16529     /**
16530      * Returns true if the dropdown list is expanded, else false.
16531      */
16532     isExpanded : function(){
16533         return this.list.isVisible();
16534     },
16535
16536     /**
16537      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16538      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16539      * @param {String} value The data value of the item to select
16540      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16541      * selected item if it is not currently in view (defaults to true)
16542      * @return {Boolean} True if the value matched an item in the list, else false
16543      */
16544     selectByValue : function(v, scrollIntoView){
16545         if(v !== undefined && v !== null){
16546             var r = this.findRecord(this.valueField || this.displayField, v);
16547             if(r){
16548                 this.select(this.store.indexOf(r), scrollIntoView);
16549                 return true;
16550             }
16551         }
16552         return false;
16553     },
16554
16555     /**
16556      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16557      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16558      * @param {Number} index The zero-based index of the list item to select
16559      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16560      * selected item if it is not currently in view (defaults to true)
16561      */
16562     select : function(index, scrollIntoView){
16563         this.selectedIndex = index;
16564         this.view.select(index);
16565         if(scrollIntoView !== false){
16566             var el = this.view.getNode(index);
16567             /*
16568              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16569              */
16570             if(el){
16571                 this.list.scrollChildIntoView(el, false);
16572             }
16573         }
16574     },
16575
16576     // private
16577     selectNext : function(){
16578         var ct = this.store.getCount();
16579         if(ct > 0){
16580             if(this.selectedIndex == -1){
16581                 this.select(0);
16582             }else if(this.selectedIndex < ct-1){
16583                 this.select(this.selectedIndex+1);
16584             }
16585         }
16586     },
16587
16588     // private
16589     selectPrev : function(){
16590         var ct = this.store.getCount();
16591         if(ct > 0){
16592             if(this.selectedIndex == -1){
16593                 this.select(0);
16594             }else if(this.selectedIndex != 0){
16595                 this.select(this.selectedIndex-1);
16596             }
16597         }
16598     },
16599
16600     // private
16601     onKeyUp : function(e){
16602         if(this.editable !== false && !e.isSpecialKey()){
16603             this.lastKey = e.getKey();
16604             this.dqTask.delay(this.queryDelay);
16605         }
16606     },
16607
16608     // private
16609     validateBlur : function(){
16610         return !this.list || !this.list.isVisible();   
16611     },
16612
16613     // private
16614     initQuery : function(){
16615         
16616         var v = this.getRawValue();
16617         
16618         if(this.tickable && this.editable){
16619             v = this.tickableInputEl().getValue();
16620         }
16621         
16622         this.doQuery(v);
16623     },
16624
16625     // private
16626     doForce : function(){
16627         if(this.inputEl().dom.value.length > 0){
16628             this.inputEl().dom.value =
16629                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16630              
16631         }
16632     },
16633
16634     /**
16635      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16636      * query allowing the query action to be canceled if needed.
16637      * @param {String} query The SQL query to execute
16638      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16639      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16640      * saved in the current store (defaults to false)
16641      */
16642     doQuery : function(q, forceAll){
16643         
16644         if(q === undefined || q === null){
16645             q = '';
16646         }
16647         var qe = {
16648             query: q,
16649             forceAll: forceAll,
16650             combo: this,
16651             cancel:false
16652         };
16653         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16654             return false;
16655         }
16656         q = qe.query;
16657         
16658         forceAll = qe.forceAll;
16659         if(forceAll === true || (q.length >= this.minChars)){
16660             
16661             this.hasQuery = true;
16662             
16663             if(this.lastQuery != q || this.alwaysQuery){
16664                 this.lastQuery = q;
16665                 if(this.mode == 'local'){
16666                     this.selectedIndex = -1;
16667                     if(forceAll){
16668                         this.store.clearFilter();
16669                     }else{
16670                         
16671                         if(this.specialFilter){
16672                             this.fireEvent('specialfilter', this);
16673                             this.onLoad();
16674                             return;
16675                         }
16676                         
16677                         this.store.filter(this.displayField, q);
16678                     }
16679                     
16680                     this.store.fireEvent("datachanged", this.store);
16681                     
16682                     this.onLoad();
16683                     
16684                     
16685                 }else{
16686                     
16687                     this.store.baseParams[this.queryParam] = q;
16688                     
16689                     var options = {params : this.getParams(q)};
16690                     
16691                     if(this.loadNext){
16692                         options.add = true;
16693                         options.params.start = this.page * this.pageSize;
16694                     }
16695                     
16696                     this.store.load(options);
16697                     
16698                     /*
16699                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16700                      *  we should expand the list on onLoad
16701                      *  so command out it
16702                      */
16703 //                    this.expand();
16704                 }
16705             }else{
16706                 this.selectedIndex = -1;
16707                 this.onLoad();   
16708             }
16709         }
16710         
16711         this.loadNext = false;
16712     },
16713     
16714     // private
16715     getParams : function(q){
16716         var p = {};
16717         //p[this.queryParam] = q;
16718         
16719         if(this.pageSize){
16720             p.start = 0;
16721             p.limit = this.pageSize;
16722         }
16723         return p;
16724     },
16725
16726     /**
16727      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16728      */
16729     collapse : function(){
16730         if(!this.isExpanded()){
16731             return;
16732         }
16733         
16734         this.list.hide();
16735         
16736         this.hasFocus = false;
16737         
16738         if(this.tickable){
16739             this.okBtn.hide();
16740             this.cancelBtn.hide();
16741             this.trigger.show();
16742             
16743             if(this.editable){
16744                 this.tickableInputEl().dom.value = '';
16745                 this.tickableInputEl().blur();
16746             }
16747             
16748         }
16749         
16750         Roo.get(document).un('mousedown', this.collapseIf, this);
16751         Roo.get(document).un('mousewheel', this.collapseIf, this);
16752         if (!this.editable) {
16753             Roo.get(document).un('keydown', this.listKeyPress, this);
16754         }
16755         this.fireEvent('collapse', this);
16756         
16757         this.validate();
16758     },
16759
16760     // private
16761     collapseIf : function(e){
16762         var in_combo  = e.within(this.el);
16763         var in_list =  e.within(this.list);
16764         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16765         
16766         if (in_combo || in_list || is_list) {
16767             //e.stopPropagation();
16768             return;
16769         }
16770         
16771         if(this.tickable){
16772             this.onTickableFooterButtonClick(e, false, false);
16773         }
16774
16775         this.collapse();
16776         
16777     },
16778
16779     /**
16780      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16781      */
16782     expand : function(){
16783        
16784         if(this.isExpanded() || !this.hasFocus){
16785             return;
16786         }
16787         
16788         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16789         this.list.setWidth(lw);
16790         
16791         Roo.log('expand');
16792         
16793         this.list.show();
16794         
16795         this.restrictHeight();
16796         
16797         if(this.tickable){
16798             
16799             this.tickItems = Roo.apply([], this.item);
16800             
16801             this.okBtn.show();
16802             this.cancelBtn.show();
16803             this.trigger.hide();
16804             
16805             if(this.editable){
16806                 this.tickableInputEl().focus();
16807             }
16808             
16809         }
16810         
16811         Roo.get(document).on('mousedown', this.collapseIf, this);
16812         Roo.get(document).on('mousewheel', this.collapseIf, this);
16813         if (!this.editable) {
16814             Roo.get(document).on('keydown', this.listKeyPress, this);
16815         }
16816         
16817         this.fireEvent('expand', this);
16818     },
16819
16820     // private
16821     // Implements the default empty TriggerField.onTriggerClick function
16822     onTriggerClick : function(e)
16823     {
16824         Roo.log('trigger click');
16825         
16826         if(this.disabled || !this.triggerList){
16827             return;
16828         }
16829         
16830         this.page = 0;
16831         this.loadNext = false;
16832         
16833         if(this.isExpanded()){
16834             this.collapse();
16835             if (!this.blockFocus) {
16836                 this.inputEl().focus();
16837             }
16838             
16839         }else {
16840             this.hasFocus = true;
16841             if(this.triggerAction == 'all') {
16842                 this.doQuery(this.allQuery, true);
16843             } else {
16844                 this.doQuery(this.getRawValue());
16845             }
16846             if (!this.blockFocus) {
16847                 this.inputEl().focus();
16848             }
16849         }
16850     },
16851     
16852     onTickableTriggerClick : function(e)
16853     {
16854         if(this.disabled){
16855             return;
16856         }
16857         
16858         this.page = 0;
16859         this.loadNext = false;
16860         this.hasFocus = true;
16861         
16862         if(this.triggerAction == 'all') {
16863             this.doQuery(this.allQuery, true);
16864         } else {
16865             this.doQuery(this.getRawValue());
16866         }
16867     },
16868     
16869     onSearchFieldClick : function(e)
16870     {
16871         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16872             this.onTickableFooterButtonClick(e, false, false);
16873             return;
16874         }
16875         
16876         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16877             return;
16878         }
16879         
16880         this.page = 0;
16881         this.loadNext = false;
16882         this.hasFocus = true;
16883         
16884         if(this.triggerAction == 'all') {
16885             this.doQuery(this.allQuery, true);
16886         } else {
16887             this.doQuery(this.getRawValue());
16888         }
16889     },
16890     
16891     listKeyPress : function(e)
16892     {
16893         //Roo.log('listkeypress');
16894         // scroll to first matching element based on key pres..
16895         if (e.isSpecialKey()) {
16896             return false;
16897         }
16898         var k = String.fromCharCode(e.getKey()).toUpperCase();
16899         //Roo.log(k);
16900         var match  = false;
16901         var csel = this.view.getSelectedNodes();
16902         var cselitem = false;
16903         if (csel.length) {
16904             var ix = this.view.indexOf(csel[0]);
16905             cselitem  = this.store.getAt(ix);
16906             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16907                 cselitem = false;
16908             }
16909             
16910         }
16911         
16912         this.store.each(function(v) { 
16913             if (cselitem) {
16914                 // start at existing selection.
16915                 if (cselitem.id == v.id) {
16916                     cselitem = false;
16917                 }
16918                 return true;
16919             }
16920                 
16921             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16922                 match = this.store.indexOf(v);
16923                 return false;
16924             }
16925             return true;
16926         }, this);
16927         
16928         if (match === false) {
16929             return true; // no more action?
16930         }
16931         // scroll to?
16932         this.view.select(match);
16933         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16934         sn.scrollIntoView(sn.dom.parentNode, false);
16935     },
16936     
16937     onViewScroll : function(e, t){
16938         
16939         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){
16940             return;
16941         }
16942         
16943         this.hasQuery = true;
16944         
16945         this.loading = this.list.select('.loading', true).first();
16946         
16947         if(this.loading === null){
16948             this.list.createChild({
16949                 tag: 'div',
16950                 cls: 'loading roo-select2-more-results roo-select2-active',
16951                 html: 'Loading more results...'
16952             });
16953             
16954             this.loading = this.list.select('.loading', true).first();
16955             
16956             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16957             
16958             this.loading.hide();
16959         }
16960         
16961         this.loading.show();
16962         
16963         var _combo = this;
16964         
16965         this.page++;
16966         this.loadNext = true;
16967         
16968         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16969         
16970         return;
16971     },
16972     
16973     addItem : function(o)
16974     {   
16975         var dv = ''; // display value
16976         
16977         if (this.displayField) {
16978             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16979         } else {
16980             // this is an error condition!!!
16981             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16982         }
16983         
16984         if(!dv.length){
16985             return;
16986         }
16987         
16988         var choice = this.choices.createChild({
16989             tag: 'li',
16990             cls: 'roo-select2-search-choice',
16991             cn: [
16992                 {
16993                     tag: 'div',
16994                     html: dv
16995                 },
16996                 {
16997                     tag: 'a',
16998                     href: '#',
16999                     cls: 'roo-select2-search-choice-close fa fa-times',
17000                     tabindex: '-1'
17001                 }
17002             ]
17003             
17004         }, this.searchField);
17005         
17006         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17007         
17008         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17009         
17010         this.item.push(o);
17011         
17012         this.lastData = o;
17013         
17014         this.syncValue();
17015         
17016         this.inputEl().dom.value = '';
17017         
17018         this.validate();
17019     },
17020     
17021     onRemoveItem : function(e, _self, o)
17022     {
17023         e.preventDefault();
17024         
17025         this.lastItem = Roo.apply([], this.item);
17026         
17027         var index = this.item.indexOf(o.data) * 1;
17028         
17029         if( index < 0){
17030             Roo.log('not this item?!');
17031             return;
17032         }
17033         
17034         this.item.splice(index, 1);
17035         o.item.remove();
17036         
17037         this.syncValue();
17038         
17039         this.fireEvent('remove', this, e);
17040         
17041         this.validate();
17042         
17043     },
17044     
17045     syncValue : function()
17046     {
17047         if(!this.item.length){
17048             this.clearValue();
17049             return;
17050         }
17051             
17052         var value = [];
17053         var _this = this;
17054         Roo.each(this.item, function(i){
17055             if(_this.valueField){
17056                 value.push(i[_this.valueField]);
17057                 return;
17058             }
17059
17060             value.push(i);
17061         });
17062
17063         this.value = value.join(',');
17064
17065         if(this.hiddenField){
17066             this.hiddenField.dom.value = this.value;
17067         }
17068         
17069         this.store.fireEvent("datachanged", this.store);
17070         
17071         this.validate();
17072     },
17073     
17074     clearItem : function()
17075     {
17076         if(!this.multiple){
17077             return;
17078         }
17079         
17080         this.item = [];
17081         
17082         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17083            c.remove();
17084         });
17085         
17086         this.syncValue();
17087         
17088         this.validate();
17089         
17090         if(this.tickable && !Roo.isTouch){
17091             this.view.refresh();
17092         }
17093     },
17094     
17095     inputEl: function ()
17096     {
17097         if(Roo.isIOS && this.useNativeIOS){
17098             return this.el.select('select.roo-ios-select', true).first();
17099         }
17100         
17101         if(Roo.isTouch && this.mobileTouchView){
17102             return this.el.select('input.form-control',true).first();
17103         }
17104         
17105         if(this.tickable){
17106             return this.searchField;
17107         }
17108         
17109         return this.el.select('input.form-control',true).first();
17110     },
17111     
17112     onTickableFooterButtonClick : function(e, btn, el)
17113     {
17114         e.preventDefault();
17115         
17116         this.lastItem = Roo.apply([], this.item);
17117         
17118         if(btn && btn.name == 'cancel'){
17119             this.tickItems = Roo.apply([], this.item);
17120             this.collapse();
17121             return;
17122         }
17123         
17124         this.clearItem();
17125         
17126         var _this = this;
17127         
17128         Roo.each(this.tickItems, function(o){
17129             _this.addItem(o);
17130         });
17131         
17132         this.collapse();
17133         
17134     },
17135     
17136     validate : function()
17137     {
17138         if(this.getVisibilityEl().hasClass('hidden')){
17139             return true;
17140         }
17141         
17142         var v = this.getRawValue();
17143         
17144         if(this.multiple){
17145             v = this.getValue();
17146         }
17147         
17148         if(this.disabled || this.allowBlank || v.length){
17149             this.markValid();
17150             return true;
17151         }
17152         
17153         this.markInvalid();
17154         return false;
17155     },
17156     
17157     tickableInputEl : function()
17158     {
17159         if(!this.tickable || !this.editable){
17160             return this.inputEl();
17161         }
17162         
17163         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17164     },
17165     
17166     
17167     getAutoCreateTouchView : function()
17168     {
17169         var id = Roo.id();
17170         
17171         var cfg = {
17172             cls: 'form-group' //input-group
17173         };
17174         
17175         var input =  {
17176             tag: 'input',
17177             id : id,
17178             type : this.inputType,
17179             cls : 'form-control x-combo-noedit',
17180             autocomplete: 'new-password',
17181             placeholder : this.placeholder || '',
17182             readonly : true
17183         };
17184         
17185         if (this.name) {
17186             input.name = this.name;
17187         }
17188         
17189         if (this.size) {
17190             input.cls += ' input-' + this.size;
17191         }
17192         
17193         if (this.disabled) {
17194             input.disabled = true;
17195         }
17196         
17197         var inputblock = {
17198             cls : 'roo-combobox-wrap',
17199             cn : [
17200                 input
17201             ]
17202         };
17203         
17204         if(this.before){
17205             inputblock.cls += ' input-group';
17206             
17207             inputblock.cn.unshift({
17208                 tag :'span',
17209                 cls : 'input-group-addon input-group-prepend input-group-text',
17210                 html : this.before
17211             });
17212         }
17213         
17214         if(this.removable && !this.multiple){
17215             inputblock.cls += ' roo-removable';
17216             
17217             inputblock.cn.push({
17218                 tag: 'button',
17219                 html : 'x',
17220                 cls : 'roo-combo-removable-btn close'
17221             });
17222         }
17223
17224         if(this.hasFeedback && !this.allowBlank){
17225             
17226             inputblock.cls += ' has-feedback';
17227             
17228             inputblock.cn.push({
17229                 tag: 'span',
17230                 cls: 'glyphicon form-control-feedback'
17231             });
17232             
17233         }
17234         
17235         if (this.after) {
17236             
17237             inputblock.cls += (this.before) ? '' : ' input-group';
17238             
17239             inputblock.cn.push({
17240                 tag :'span',
17241                 cls : 'input-group-addon input-group-append input-group-text',
17242                 html : this.after
17243             });
17244         }
17245
17246         
17247         var ibwrap = inputblock;
17248         
17249         if(this.multiple){
17250             ibwrap = {
17251                 tag: 'ul',
17252                 cls: 'roo-select2-choices',
17253                 cn:[
17254                     {
17255                         tag: 'li',
17256                         cls: 'roo-select2-search-field',
17257                         cn: [
17258
17259                             inputblock
17260                         ]
17261                     }
17262                 ]
17263             };
17264         
17265             
17266         }
17267         
17268         var combobox = {
17269             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17270             cn: [
17271                 {
17272                     tag: 'input',
17273                     type : 'hidden',
17274                     cls: 'form-hidden-field'
17275                 },
17276                 ibwrap
17277             ]
17278         };
17279         
17280         if(!this.multiple && this.showToggleBtn){
17281             
17282             var caret = {
17283                 cls: 'caret'
17284             };
17285             
17286             if (this.caret != false) {
17287                 caret = {
17288                      tag: 'i',
17289                      cls: 'fa fa-' + this.caret
17290                 };
17291                 
17292             }
17293             
17294             combobox.cn.push({
17295                 tag :'span',
17296                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17297                 cn : [
17298                     Roo.bootstrap.version == 3 ? caret : '',
17299                     {
17300                         tag: 'span',
17301                         cls: 'combobox-clear',
17302                         cn  : [
17303                             {
17304                                 tag : 'i',
17305                                 cls: 'icon-remove'
17306                             }
17307                         ]
17308                     }
17309                 ]
17310
17311             })
17312         }
17313         
17314         if(this.multiple){
17315             combobox.cls += ' roo-select2-container-multi';
17316         }
17317         
17318         var align = this.labelAlign || this.parentLabelAlign();
17319         
17320         if (align ==='left' && this.fieldLabel.length) {
17321
17322             cfg.cn = [
17323                 {
17324                    tag : 'i',
17325                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17326                    tooltip : 'This field is required'
17327                 },
17328                 {
17329                     tag: 'label',
17330                     cls : 'control-label col-form-label',
17331                     html : this.fieldLabel
17332
17333                 },
17334                 {
17335                     cls : 'roo-combobox-wrap ', 
17336                     cn: [
17337                         combobox
17338                     ]
17339                 }
17340             ];
17341             
17342             var labelCfg = cfg.cn[1];
17343             var contentCfg = cfg.cn[2];
17344             
17345
17346             if(this.indicatorpos == 'right'){
17347                 cfg.cn = [
17348                     {
17349                         tag: 'label',
17350                         'for' :  id,
17351                         cls : 'control-label col-form-label',
17352                         cn : [
17353                             {
17354                                 tag : 'span',
17355                                 html : this.fieldLabel
17356                             },
17357                             {
17358                                 tag : 'i',
17359                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17360                                 tooltip : 'This field is required'
17361                             }
17362                         ]
17363                     },
17364                     {
17365                         cls : "roo-combobox-wrap ",
17366                         cn: [
17367                             combobox
17368                         ]
17369                     }
17370
17371                 ];
17372                 
17373                 labelCfg = cfg.cn[0];
17374                 contentCfg = cfg.cn[1];
17375             }
17376             
17377            
17378             
17379             if(this.labelWidth > 12){
17380                 labelCfg.style = "width: " + this.labelWidth + 'px';
17381             }
17382            
17383             if(this.labelWidth < 13 && this.labelmd == 0){
17384                 this.labelmd = this.labelWidth;
17385             }
17386             
17387             if(this.labellg > 0){
17388                 labelCfg.cls += ' col-lg-' + this.labellg;
17389                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17390             }
17391             
17392             if(this.labelmd > 0){
17393                 labelCfg.cls += ' col-md-' + this.labelmd;
17394                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17395             }
17396             
17397             if(this.labelsm > 0){
17398                 labelCfg.cls += ' col-sm-' + this.labelsm;
17399                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17400             }
17401             
17402             if(this.labelxs > 0){
17403                 labelCfg.cls += ' col-xs-' + this.labelxs;
17404                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17405             }
17406                 
17407                 
17408         } else if ( this.fieldLabel.length) {
17409             cfg.cn = [
17410                 {
17411                    tag : 'i',
17412                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17413                    tooltip : 'This field is required'
17414                 },
17415                 {
17416                     tag: 'label',
17417                     cls : 'control-label',
17418                     html : this.fieldLabel
17419
17420                 },
17421                 {
17422                     cls : '', 
17423                     cn: [
17424                         combobox
17425                     ]
17426                 }
17427             ];
17428             
17429             if(this.indicatorpos == 'right'){
17430                 cfg.cn = [
17431                     {
17432                         tag: 'label',
17433                         cls : 'control-label',
17434                         html : this.fieldLabel,
17435                         cn : [
17436                             {
17437                                tag : 'i',
17438                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17439                                tooltip : 'This field is required'
17440                             }
17441                         ]
17442                     },
17443                     {
17444                         cls : '', 
17445                         cn: [
17446                             combobox
17447                         ]
17448                     }
17449                 ];
17450             }
17451         } else {
17452             cfg.cn = combobox;    
17453         }
17454         
17455         
17456         var settings = this;
17457         
17458         ['xs','sm','md','lg'].map(function(size){
17459             if (settings[size]) {
17460                 cfg.cls += ' col-' + size + '-' + settings[size];
17461             }
17462         });
17463         
17464         return cfg;
17465     },
17466     
17467     initTouchView : function()
17468     {
17469         this.renderTouchView();
17470         
17471         this.touchViewEl.on('scroll', function(){
17472             this.el.dom.scrollTop = 0;
17473         }, this);
17474         
17475         this.originalValue = this.getValue();
17476         
17477         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17478         
17479         this.inputEl().on("click", this.showTouchView, this);
17480         if (this.triggerEl) {
17481             this.triggerEl.on("click", this.showTouchView, this);
17482         }
17483         
17484         
17485         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17486         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17487         
17488         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17489         
17490         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17491         this.store.on('load', this.onTouchViewLoad, this);
17492         this.store.on('loadexception', this.onTouchViewLoadException, this);
17493         
17494         if(this.hiddenName){
17495             
17496             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17497             
17498             this.hiddenField.dom.value =
17499                 this.hiddenValue !== undefined ? this.hiddenValue :
17500                 this.value !== undefined ? this.value : '';
17501         
17502             this.el.dom.removeAttribute('name');
17503             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17504         }
17505         
17506         if(this.multiple){
17507             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17508             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17509         }
17510         
17511         if(this.removable && !this.multiple){
17512             var close = this.closeTriggerEl();
17513             if(close){
17514                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17515                 close.on('click', this.removeBtnClick, this, close);
17516             }
17517         }
17518         /*
17519          * fix the bug in Safari iOS8
17520          */
17521         this.inputEl().on("focus", function(e){
17522             document.activeElement.blur();
17523         }, this);
17524         
17525         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17526         
17527         return;
17528         
17529         
17530     },
17531     
17532     renderTouchView : function()
17533     {
17534         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17535         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17536         
17537         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17538         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17539         
17540         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17541         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17542         this.touchViewBodyEl.setStyle('overflow', 'auto');
17543         
17544         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17545         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17546         
17547         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17548         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17549         
17550     },
17551     
17552     showTouchView : function()
17553     {
17554         if(this.disabled){
17555             return;
17556         }
17557         
17558         this.touchViewHeaderEl.hide();
17559
17560         if(this.modalTitle.length){
17561             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17562             this.touchViewHeaderEl.show();
17563         }
17564
17565         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17566         this.touchViewEl.show();
17567
17568         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17569         
17570         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17571         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17572
17573         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17574
17575         if(this.modalTitle.length){
17576             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17577         }
17578         
17579         this.touchViewBodyEl.setHeight(bodyHeight);
17580
17581         if(this.animate){
17582             var _this = this;
17583             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17584         }else{
17585             this.touchViewEl.addClass(['in','show']);
17586         }
17587         
17588         if(this._touchViewMask){
17589             Roo.get(document.body).addClass("x-body-masked");
17590             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17591             this._touchViewMask.setStyle('z-index', 10000);
17592             this._touchViewMask.addClass('show');
17593         }
17594         
17595         this.doTouchViewQuery();
17596         
17597     },
17598     
17599     hideTouchView : function()
17600     {
17601         this.touchViewEl.removeClass(['in','show']);
17602
17603         if(this.animate){
17604             var _this = this;
17605             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17606         }else{
17607             this.touchViewEl.setStyle('display', 'none');
17608         }
17609         
17610         if(this._touchViewMask){
17611             this._touchViewMask.removeClass('show');
17612             Roo.get(document.body).removeClass("x-body-masked");
17613         }
17614     },
17615     
17616     setTouchViewValue : function()
17617     {
17618         if(this.multiple){
17619             this.clearItem();
17620         
17621             var _this = this;
17622
17623             Roo.each(this.tickItems, function(o){
17624                 this.addItem(o);
17625             }, this);
17626         }
17627         
17628         this.hideTouchView();
17629     },
17630     
17631     doTouchViewQuery : function()
17632     {
17633         var qe = {
17634             query: '',
17635             forceAll: true,
17636             combo: this,
17637             cancel:false
17638         };
17639         
17640         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17641             return false;
17642         }
17643         
17644         if(!this.alwaysQuery || this.mode == 'local'){
17645             this.onTouchViewLoad();
17646             return;
17647         }
17648         
17649         this.store.load();
17650     },
17651     
17652     onTouchViewBeforeLoad : function(combo,opts)
17653     {
17654         return;
17655     },
17656
17657     // private
17658     onTouchViewLoad : function()
17659     {
17660         if(this.store.getCount() < 1){
17661             this.onTouchViewEmptyResults();
17662             return;
17663         }
17664         
17665         this.clearTouchView();
17666         
17667         var rawValue = this.getRawValue();
17668         
17669         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17670         
17671         this.tickItems = [];
17672         
17673         this.store.data.each(function(d, rowIndex){
17674             var row = this.touchViewListGroup.createChild(template);
17675             
17676             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17677                 row.addClass(d.data.cls);
17678             }
17679             
17680             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17681                 var cfg = {
17682                     data : d.data,
17683                     html : d.data[this.displayField]
17684                 };
17685                 
17686                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17687                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17688                 }
17689             }
17690             row.removeClass('selected');
17691             if(!this.multiple && this.valueField &&
17692                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17693             {
17694                 // radio buttons..
17695                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17696                 row.addClass('selected');
17697             }
17698             
17699             if(this.multiple && this.valueField &&
17700                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17701             {
17702                 
17703                 // checkboxes...
17704                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17705                 this.tickItems.push(d.data);
17706             }
17707             
17708             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17709             
17710         }, this);
17711         
17712         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17713         
17714         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17715
17716         if(this.modalTitle.length){
17717             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17718         }
17719
17720         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17721         
17722         if(this.mobile_restrict_height && listHeight < bodyHeight){
17723             this.touchViewBodyEl.setHeight(listHeight);
17724         }
17725         
17726         var _this = this;
17727         
17728         if(firstChecked && listHeight > bodyHeight){
17729             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17730         }
17731         
17732     },
17733     
17734     onTouchViewLoadException : function()
17735     {
17736         this.hideTouchView();
17737     },
17738     
17739     onTouchViewEmptyResults : function()
17740     {
17741         this.clearTouchView();
17742         
17743         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17744         
17745         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17746         
17747     },
17748     
17749     clearTouchView : function()
17750     {
17751         this.touchViewListGroup.dom.innerHTML = '';
17752     },
17753     
17754     onTouchViewClick : function(e, el, o)
17755     {
17756         e.preventDefault();
17757         
17758         var row = o.row;
17759         var rowIndex = o.rowIndex;
17760         
17761         var r = this.store.getAt(rowIndex);
17762         
17763         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17764             
17765             if(!this.multiple){
17766                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17767                     c.dom.removeAttribute('checked');
17768                 }, this);
17769
17770                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17771
17772                 this.setFromData(r.data);
17773
17774                 var close = this.closeTriggerEl();
17775
17776                 if(close){
17777                     close.show();
17778                 }
17779
17780                 this.hideTouchView();
17781
17782                 this.fireEvent('select', this, r, rowIndex);
17783
17784                 return;
17785             }
17786
17787             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17788                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17789                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17790                 return;
17791             }
17792
17793             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17794             this.addItem(r.data);
17795             this.tickItems.push(r.data);
17796         }
17797     },
17798     
17799     getAutoCreateNativeIOS : function()
17800     {
17801         var cfg = {
17802             cls: 'form-group' //input-group,
17803         };
17804         
17805         var combobox =  {
17806             tag: 'select',
17807             cls : 'roo-ios-select'
17808         };
17809         
17810         if (this.name) {
17811             combobox.name = this.name;
17812         }
17813         
17814         if (this.disabled) {
17815             combobox.disabled = true;
17816         }
17817         
17818         var settings = this;
17819         
17820         ['xs','sm','md','lg'].map(function(size){
17821             if (settings[size]) {
17822                 cfg.cls += ' col-' + size + '-' + settings[size];
17823             }
17824         });
17825         
17826         cfg.cn = combobox;
17827         
17828         return cfg;
17829         
17830     },
17831     
17832     initIOSView : function()
17833     {
17834         this.store.on('load', this.onIOSViewLoad, this);
17835         
17836         return;
17837     },
17838     
17839     onIOSViewLoad : function()
17840     {
17841         if(this.store.getCount() < 1){
17842             return;
17843         }
17844         
17845         this.clearIOSView();
17846         
17847         if(this.allowBlank) {
17848             
17849             var default_text = '-- SELECT --';
17850             
17851             if(this.placeholder.length){
17852                 default_text = this.placeholder;
17853             }
17854             
17855             if(this.emptyTitle.length){
17856                 default_text += ' - ' + this.emptyTitle + ' -';
17857             }
17858             
17859             var opt = this.inputEl().createChild({
17860                 tag: 'option',
17861                 value : 0,
17862                 html : default_text
17863             });
17864             
17865             var o = {};
17866             o[this.valueField] = 0;
17867             o[this.displayField] = default_text;
17868             
17869             this.ios_options.push({
17870                 data : o,
17871                 el : opt
17872             });
17873             
17874         }
17875         
17876         this.store.data.each(function(d, rowIndex){
17877             
17878             var html = '';
17879             
17880             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17881                 html = d.data[this.displayField];
17882             }
17883             
17884             var value = '';
17885             
17886             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17887                 value = d.data[this.valueField];
17888             }
17889             
17890             var option = {
17891                 tag: 'option',
17892                 value : value,
17893                 html : html
17894             };
17895             
17896             if(this.value == d.data[this.valueField]){
17897                 option['selected'] = true;
17898             }
17899             
17900             var opt = this.inputEl().createChild(option);
17901             
17902             this.ios_options.push({
17903                 data : d.data,
17904                 el : opt
17905             });
17906             
17907         }, this);
17908         
17909         this.inputEl().on('change', function(){
17910            this.fireEvent('select', this);
17911         }, this);
17912         
17913     },
17914     
17915     clearIOSView: function()
17916     {
17917         this.inputEl().dom.innerHTML = '';
17918         
17919         this.ios_options = [];
17920     },
17921     
17922     setIOSValue: function(v)
17923     {
17924         this.value = v;
17925         
17926         if(!this.ios_options){
17927             return;
17928         }
17929         
17930         Roo.each(this.ios_options, function(opts){
17931            
17932            opts.el.dom.removeAttribute('selected');
17933            
17934            if(opts.data[this.valueField] != v){
17935                return;
17936            }
17937            
17938            opts.el.dom.setAttribute('selected', true);
17939            
17940         }, this);
17941     }
17942
17943     /** 
17944     * @cfg {Boolean} grow 
17945     * @hide 
17946     */
17947     /** 
17948     * @cfg {Number} growMin 
17949     * @hide 
17950     */
17951     /** 
17952     * @cfg {Number} growMax 
17953     * @hide 
17954     */
17955     /**
17956      * @hide
17957      * @method autoSize
17958      */
17959 });
17960
17961 Roo.apply(Roo.bootstrap.ComboBox,  {
17962     
17963     header : {
17964         tag: 'div',
17965         cls: 'modal-header',
17966         cn: [
17967             {
17968                 tag: 'h4',
17969                 cls: 'modal-title'
17970             }
17971         ]
17972     },
17973     
17974     body : {
17975         tag: 'div',
17976         cls: 'modal-body',
17977         cn: [
17978             {
17979                 tag: 'ul',
17980                 cls: 'list-group'
17981             }
17982         ]
17983     },
17984     
17985     listItemRadio : {
17986         tag: 'li',
17987         cls: 'list-group-item',
17988         cn: [
17989             {
17990                 tag: 'span',
17991                 cls: 'roo-combobox-list-group-item-value'
17992             },
17993             {
17994                 tag: 'div',
17995                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17996                 cn: [
17997                     {
17998                         tag: 'input',
17999                         type: 'radio'
18000                     },
18001                     {
18002                         tag: 'label'
18003                     }
18004                 ]
18005             }
18006         ]
18007     },
18008     
18009     listItemCheckbox : {
18010         tag: 'li',
18011         cls: 'list-group-item',
18012         cn: [
18013             {
18014                 tag: 'span',
18015                 cls: 'roo-combobox-list-group-item-value'
18016             },
18017             {
18018                 tag: 'div',
18019                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18020                 cn: [
18021                     {
18022                         tag: 'input',
18023                         type: 'checkbox'
18024                     },
18025                     {
18026                         tag: 'label'
18027                     }
18028                 ]
18029             }
18030         ]
18031     },
18032     
18033     emptyResult : {
18034         tag: 'div',
18035         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18036     },
18037     
18038     footer : {
18039         tag: 'div',
18040         cls: 'modal-footer',
18041         cn: [
18042             {
18043                 tag: 'div',
18044                 cls: 'row',
18045                 cn: [
18046                     {
18047                         tag: 'div',
18048                         cls: 'col-xs-6 text-left',
18049                         cn: {
18050                             tag: 'button',
18051                             cls: 'btn btn-danger roo-touch-view-cancel',
18052                             html: 'Cancel'
18053                         }
18054                     },
18055                     {
18056                         tag: 'div',
18057                         cls: 'col-xs-6 text-right',
18058                         cn: {
18059                             tag: 'button',
18060                             cls: 'btn btn-success roo-touch-view-ok',
18061                             html: 'OK'
18062                         }
18063                     }
18064                 ]
18065             }
18066         ]
18067         
18068     }
18069 });
18070
18071 Roo.apply(Roo.bootstrap.ComboBox,  {
18072     
18073     touchViewTemplate : {
18074         tag: 'div',
18075         cls: 'modal fade roo-combobox-touch-view',
18076         cn: [
18077             {
18078                 tag: 'div',
18079                 cls: 'modal-dialog',
18080                 style : 'position:fixed', // we have to fix position....
18081                 cn: [
18082                     {
18083                         tag: 'div',
18084                         cls: 'modal-content',
18085                         cn: [
18086                             Roo.bootstrap.ComboBox.header,
18087                             Roo.bootstrap.ComboBox.body,
18088                             Roo.bootstrap.ComboBox.footer
18089                         ]
18090                     }
18091                 ]
18092             }
18093         ]
18094     }
18095 });/*
18096  * Based on:
18097  * Ext JS Library 1.1.1
18098  * Copyright(c) 2006-2007, Ext JS, LLC.
18099  *
18100  * Originally Released Under LGPL - original licence link has changed is not relivant.
18101  *
18102  * Fork - LGPL
18103  * <script type="text/javascript">
18104  */
18105
18106 /**
18107  * @class Roo.View
18108  * @extends Roo.util.Observable
18109  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18110  * This class also supports single and multi selection modes. <br>
18111  * Create a data model bound view:
18112  <pre><code>
18113  var store = new Roo.data.Store(...);
18114
18115  var view = new Roo.View({
18116     el : "my-element",
18117     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18118  
18119     singleSelect: true,
18120     selectedClass: "ydataview-selected",
18121     store: store
18122  });
18123
18124  // listen for node click?
18125  view.on("click", function(vw, index, node, e){
18126  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18127  });
18128
18129  // load XML data
18130  dataModel.load("foobar.xml");
18131  </code></pre>
18132  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18133  * <br><br>
18134  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18135  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18136  * 
18137  * Note: old style constructor is still suported (container, template, config)
18138  * 
18139  * @constructor
18140  * Create a new View
18141  * @param {Object} config The config object
18142  * 
18143  */
18144 Roo.View = function(config, depreciated_tpl, depreciated_config){
18145     
18146     this.parent = false;
18147     
18148     if (typeof(depreciated_tpl) == 'undefined') {
18149         // new way.. - universal constructor.
18150         Roo.apply(this, config);
18151         this.el  = Roo.get(this.el);
18152     } else {
18153         // old format..
18154         this.el  = Roo.get(config);
18155         this.tpl = depreciated_tpl;
18156         Roo.apply(this, depreciated_config);
18157     }
18158     this.wrapEl  = this.el.wrap().wrap();
18159     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18160     
18161     
18162     if(typeof(this.tpl) == "string"){
18163         this.tpl = new Roo.Template(this.tpl);
18164     } else {
18165         // support xtype ctors..
18166         this.tpl = new Roo.factory(this.tpl, Roo);
18167     }
18168     
18169     
18170     this.tpl.compile();
18171     
18172     /** @private */
18173     this.addEvents({
18174         /**
18175          * @event beforeclick
18176          * Fires before a click is processed. Returns false to cancel the default action.
18177          * @param {Roo.View} this
18178          * @param {Number} index The index of the target node
18179          * @param {HTMLElement} node The target node
18180          * @param {Roo.EventObject} e The raw event object
18181          */
18182             "beforeclick" : true,
18183         /**
18184          * @event click
18185          * Fires when a template node is clicked.
18186          * @param {Roo.View} this
18187          * @param {Number} index The index of the target node
18188          * @param {HTMLElement} node The target node
18189          * @param {Roo.EventObject} e The raw event object
18190          */
18191             "click" : true,
18192         /**
18193          * @event dblclick
18194          * Fires when a template node is double clicked.
18195          * @param {Roo.View} this
18196          * @param {Number} index The index of the target node
18197          * @param {HTMLElement} node The target node
18198          * @param {Roo.EventObject} e The raw event object
18199          */
18200             "dblclick" : true,
18201         /**
18202          * @event contextmenu
18203          * Fires when a template node is right clicked.
18204          * @param {Roo.View} this
18205          * @param {Number} index The index of the target node
18206          * @param {HTMLElement} node The target node
18207          * @param {Roo.EventObject} e The raw event object
18208          */
18209             "contextmenu" : true,
18210         /**
18211          * @event selectionchange
18212          * Fires when the selected nodes change.
18213          * @param {Roo.View} this
18214          * @param {Array} selections Array of the selected nodes
18215          */
18216             "selectionchange" : true,
18217     
18218         /**
18219          * @event beforeselect
18220          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18221          * @param {Roo.View} this
18222          * @param {HTMLElement} node The node to be selected
18223          * @param {Array} selections Array of currently selected nodes
18224          */
18225             "beforeselect" : true,
18226         /**
18227          * @event preparedata
18228          * Fires on every row to render, to allow you to change the data.
18229          * @param {Roo.View} this
18230          * @param {Object} data to be rendered (change this)
18231          */
18232           "preparedata" : true
18233           
18234           
18235         });
18236
18237
18238
18239     this.el.on({
18240         "click": this.onClick,
18241         "dblclick": this.onDblClick,
18242         "contextmenu": this.onContextMenu,
18243         scope:this
18244     });
18245
18246     this.selections = [];
18247     this.nodes = [];
18248     this.cmp = new Roo.CompositeElementLite([]);
18249     if(this.store){
18250         this.store = Roo.factory(this.store, Roo.data);
18251         this.setStore(this.store, true);
18252     }
18253     
18254     if ( this.footer && this.footer.xtype) {
18255            
18256          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18257         
18258         this.footer.dataSource = this.store;
18259         this.footer.container = fctr;
18260         this.footer = Roo.factory(this.footer, Roo);
18261         fctr.insertFirst(this.el);
18262         
18263         // this is a bit insane - as the paging toolbar seems to detach the el..
18264 //        dom.parentNode.parentNode.parentNode
18265          // they get detached?
18266     }
18267     
18268     
18269     Roo.View.superclass.constructor.call(this);
18270     
18271     
18272 };
18273
18274 Roo.extend(Roo.View, Roo.util.Observable, {
18275     
18276      /**
18277      * @cfg {Roo.data.Store} store Data store to load data from.
18278      */
18279     store : false,
18280     
18281     /**
18282      * @cfg {String|Roo.Element} el The container element.
18283      */
18284     el : '',
18285     
18286     /**
18287      * @cfg {String|Roo.Template} tpl The template used by this View 
18288      */
18289     tpl : false,
18290     /**
18291      * @cfg {String} dataName the named area of the template to use as the data area
18292      *                          Works with domtemplates roo-name="name"
18293      */
18294     dataName: false,
18295     /**
18296      * @cfg {String} selectedClass The css class to add to selected nodes
18297      */
18298     selectedClass : "x-view-selected",
18299      /**
18300      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18301      */
18302     emptyText : "",
18303     
18304     /**
18305      * @cfg {String} text to display on mask (default Loading)
18306      */
18307     mask : false,
18308     /**
18309      * @cfg {Boolean} multiSelect Allow multiple selection
18310      */
18311     multiSelect : false,
18312     /**
18313      * @cfg {Boolean} singleSelect Allow single selection
18314      */
18315     singleSelect:  false,
18316     
18317     /**
18318      * @cfg {Boolean} toggleSelect - selecting 
18319      */
18320     toggleSelect : false,
18321     
18322     /**
18323      * @cfg {Boolean} tickable - selecting 
18324      */
18325     tickable : false,
18326     
18327     /**
18328      * Returns the element this view is bound to.
18329      * @return {Roo.Element}
18330      */
18331     getEl : function(){
18332         return this.wrapEl;
18333     },
18334     
18335     
18336
18337     /**
18338      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18339      */
18340     refresh : function(){
18341         //Roo.log('refresh');
18342         var t = this.tpl;
18343         
18344         // if we are using something like 'domtemplate', then
18345         // the what gets used is:
18346         // t.applySubtemplate(NAME, data, wrapping data..)
18347         // the outer template then get' applied with
18348         //     the store 'extra data'
18349         // and the body get's added to the
18350         //      roo-name="data" node?
18351         //      <span class='roo-tpl-{name}'></span> ?????
18352         
18353         
18354         
18355         this.clearSelections();
18356         this.el.update("");
18357         var html = [];
18358         var records = this.store.getRange();
18359         if(records.length < 1) {
18360             
18361             // is this valid??  = should it render a template??
18362             
18363             this.el.update(this.emptyText);
18364             return;
18365         }
18366         var el = this.el;
18367         if (this.dataName) {
18368             this.el.update(t.apply(this.store.meta)); //????
18369             el = this.el.child('.roo-tpl-' + this.dataName);
18370         }
18371         
18372         for(var i = 0, len = records.length; i < len; i++){
18373             var data = this.prepareData(records[i].data, i, records[i]);
18374             this.fireEvent("preparedata", this, data, i, records[i]);
18375             
18376             var d = Roo.apply({}, data);
18377             
18378             if(this.tickable){
18379                 Roo.apply(d, {'roo-id' : Roo.id()});
18380                 
18381                 var _this = this;
18382             
18383                 Roo.each(this.parent.item, function(item){
18384                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18385                         return;
18386                     }
18387                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18388                 });
18389             }
18390             
18391             html[html.length] = Roo.util.Format.trim(
18392                 this.dataName ?
18393                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18394                     t.apply(d)
18395             );
18396         }
18397         
18398         
18399         
18400         el.update(html.join(""));
18401         this.nodes = el.dom.childNodes;
18402         this.updateIndexes(0);
18403     },
18404     
18405
18406     /**
18407      * Function to override to reformat the data that is sent to
18408      * the template for each node.
18409      * DEPRICATED - use the preparedata event handler.
18410      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18411      * a JSON object for an UpdateManager bound view).
18412      */
18413     prepareData : function(data, index, record)
18414     {
18415         this.fireEvent("preparedata", this, data, index, record);
18416         return data;
18417     },
18418
18419     onUpdate : function(ds, record){
18420         // Roo.log('on update');   
18421         this.clearSelections();
18422         var index = this.store.indexOf(record);
18423         var n = this.nodes[index];
18424         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18425         n.parentNode.removeChild(n);
18426         this.updateIndexes(index, index);
18427     },
18428
18429     
18430     
18431 // --------- FIXME     
18432     onAdd : function(ds, records, index)
18433     {
18434         //Roo.log(['on Add', ds, records, index] );        
18435         this.clearSelections();
18436         if(this.nodes.length == 0){
18437             this.refresh();
18438             return;
18439         }
18440         var n = this.nodes[index];
18441         for(var i = 0, len = records.length; i < len; i++){
18442             var d = this.prepareData(records[i].data, i, records[i]);
18443             if(n){
18444                 this.tpl.insertBefore(n, d);
18445             }else{
18446                 
18447                 this.tpl.append(this.el, d);
18448             }
18449         }
18450         this.updateIndexes(index);
18451     },
18452
18453     onRemove : function(ds, record, index){
18454        // Roo.log('onRemove');
18455         this.clearSelections();
18456         var el = this.dataName  ?
18457             this.el.child('.roo-tpl-' + this.dataName) :
18458             this.el; 
18459         
18460         el.dom.removeChild(this.nodes[index]);
18461         this.updateIndexes(index);
18462     },
18463
18464     /**
18465      * Refresh an individual node.
18466      * @param {Number} index
18467      */
18468     refreshNode : function(index){
18469         this.onUpdate(this.store, this.store.getAt(index));
18470     },
18471
18472     updateIndexes : function(startIndex, endIndex){
18473         var ns = this.nodes;
18474         startIndex = startIndex || 0;
18475         endIndex = endIndex || ns.length - 1;
18476         for(var i = startIndex; i <= endIndex; i++){
18477             ns[i].nodeIndex = i;
18478         }
18479     },
18480
18481     /**
18482      * Changes the data store this view uses and refresh the view.
18483      * @param {Store} store
18484      */
18485     setStore : function(store, initial){
18486         if(!initial && this.store){
18487             this.store.un("datachanged", this.refresh);
18488             this.store.un("add", this.onAdd);
18489             this.store.un("remove", this.onRemove);
18490             this.store.un("update", this.onUpdate);
18491             this.store.un("clear", this.refresh);
18492             this.store.un("beforeload", this.onBeforeLoad);
18493             this.store.un("load", this.onLoad);
18494             this.store.un("loadexception", this.onLoad);
18495         }
18496         if(store){
18497           
18498             store.on("datachanged", this.refresh, this);
18499             store.on("add", this.onAdd, this);
18500             store.on("remove", this.onRemove, this);
18501             store.on("update", this.onUpdate, this);
18502             store.on("clear", this.refresh, this);
18503             store.on("beforeload", this.onBeforeLoad, this);
18504             store.on("load", this.onLoad, this);
18505             store.on("loadexception", this.onLoad, this);
18506         }
18507         
18508         if(store){
18509             this.refresh();
18510         }
18511     },
18512     /**
18513      * onbeforeLoad - masks the loading area.
18514      *
18515      */
18516     onBeforeLoad : function(store,opts)
18517     {
18518          //Roo.log('onBeforeLoad');   
18519         if (!opts.add) {
18520             this.el.update("");
18521         }
18522         this.el.mask(this.mask ? this.mask : "Loading" ); 
18523     },
18524     onLoad : function ()
18525     {
18526         this.el.unmask();
18527     },
18528     
18529
18530     /**
18531      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18532      * @param {HTMLElement} node
18533      * @return {HTMLElement} The template node
18534      */
18535     findItemFromChild : function(node){
18536         var el = this.dataName  ?
18537             this.el.child('.roo-tpl-' + this.dataName,true) :
18538             this.el.dom; 
18539         
18540         if(!node || node.parentNode == el){
18541                     return node;
18542             }
18543             var p = node.parentNode;
18544             while(p && p != el){
18545             if(p.parentNode == el){
18546                 return p;
18547             }
18548             p = p.parentNode;
18549         }
18550             return null;
18551     },
18552
18553     /** @ignore */
18554     onClick : function(e){
18555         var item = this.findItemFromChild(e.getTarget());
18556         if(item){
18557             var index = this.indexOf(item);
18558             if(this.onItemClick(item, index, e) !== false){
18559                 this.fireEvent("click", this, index, item, e);
18560             }
18561         }else{
18562             this.clearSelections();
18563         }
18564     },
18565
18566     /** @ignore */
18567     onContextMenu : function(e){
18568         var item = this.findItemFromChild(e.getTarget());
18569         if(item){
18570             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18571         }
18572     },
18573
18574     /** @ignore */
18575     onDblClick : function(e){
18576         var item = this.findItemFromChild(e.getTarget());
18577         if(item){
18578             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18579         }
18580     },
18581
18582     onItemClick : function(item, index, e)
18583     {
18584         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18585             return false;
18586         }
18587         if (this.toggleSelect) {
18588             var m = this.isSelected(item) ? 'unselect' : 'select';
18589             //Roo.log(m);
18590             var _t = this;
18591             _t[m](item, true, false);
18592             return true;
18593         }
18594         if(this.multiSelect || this.singleSelect){
18595             if(this.multiSelect && e.shiftKey && this.lastSelection){
18596                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18597             }else{
18598                 this.select(item, this.multiSelect && e.ctrlKey);
18599                 this.lastSelection = item;
18600             }
18601             
18602             if(!this.tickable){
18603                 e.preventDefault();
18604             }
18605             
18606         }
18607         return true;
18608     },
18609
18610     /**
18611      * Get the number of selected nodes.
18612      * @return {Number}
18613      */
18614     getSelectionCount : function(){
18615         return this.selections.length;
18616     },
18617
18618     /**
18619      * Get the currently selected nodes.
18620      * @return {Array} An array of HTMLElements
18621      */
18622     getSelectedNodes : function(){
18623         return this.selections;
18624     },
18625
18626     /**
18627      * Get the indexes of the selected nodes.
18628      * @return {Array}
18629      */
18630     getSelectedIndexes : function(){
18631         var indexes = [], s = this.selections;
18632         for(var i = 0, len = s.length; i < len; i++){
18633             indexes.push(s[i].nodeIndex);
18634         }
18635         return indexes;
18636     },
18637
18638     /**
18639      * Clear all selections
18640      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18641      */
18642     clearSelections : function(suppressEvent){
18643         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18644             this.cmp.elements = this.selections;
18645             this.cmp.removeClass(this.selectedClass);
18646             this.selections = [];
18647             if(!suppressEvent){
18648                 this.fireEvent("selectionchange", this, this.selections);
18649             }
18650         }
18651     },
18652
18653     /**
18654      * Returns true if the passed node is selected
18655      * @param {HTMLElement/Number} node The node or node index
18656      * @return {Boolean}
18657      */
18658     isSelected : function(node){
18659         var s = this.selections;
18660         if(s.length < 1){
18661             return false;
18662         }
18663         node = this.getNode(node);
18664         return s.indexOf(node) !== -1;
18665     },
18666
18667     /**
18668      * Selects nodes.
18669      * @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
18670      * @param {Boolean} keepExisting (optional) true to keep existing selections
18671      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18672      */
18673     select : function(nodeInfo, keepExisting, suppressEvent){
18674         if(nodeInfo instanceof Array){
18675             if(!keepExisting){
18676                 this.clearSelections(true);
18677             }
18678             for(var i = 0, len = nodeInfo.length; i < len; i++){
18679                 this.select(nodeInfo[i], true, true);
18680             }
18681             return;
18682         } 
18683         var node = this.getNode(nodeInfo);
18684         if(!node || this.isSelected(node)){
18685             return; // already selected.
18686         }
18687         if(!keepExisting){
18688             this.clearSelections(true);
18689         }
18690         
18691         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18692             Roo.fly(node).addClass(this.selectedClass);
18693             this.selections.push(node);
18694             if(!suppressEvent){
18695                 this.fireEvent("selectionchange", this, this.selections);
18696             }
18697         }
18698         
18699         
18700     },
18701       /**
18702      * Unselects nodes.
18703      * @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
18704      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18705      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18706      */
18707     unselect : function(nodeInfo, keepExisting, suppressEvent)
18708     {
18709         if(nodeInfo instanceof Array){
18710             Roo.each(this.selections, function(s) {
18711                 this.unselect(s, nodeInfo);
18712             }, this);
18713             return;
18714         }
18715         var node = this.getNode(nodeInfo);
18716         if(!node || !this.isSelected(node)){
18717             //Roo.log("not selected");
18718             return; // not selected.
18719         }
18720         // fireevent???
18721         var ns = [];
18722         Roo.each(this.selections, function(s) {
18723             if (s == node ) {
18724                 Roo.fly(node).removeClass(this.selectedClass);
18725
18726                 return;
18727             }
18728             ns.push(s);
18729         },this);
18730         
18731         this.selections= ns;
18732         this.fireEvent("selectionchange", this, this.selections);
18733     },
18734
18735     /**
18736      * Gets a template node.
18737      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18738      * @return {HTMLElement} The node or null if it wasn't found
18739      */
18740     getNode : function(nodeInfo){
18741         if(typeof nodeInfo == "string"){
18742             return document.getElementById(nodeInfo);
18743         }else if(typeof nodeInfo == "number"){
18744             return this.nodes[nodeInfo];
18745         }
18746         return nodeInfo;
18747     },
18748
18749     /**
18750      * Gets a range template nodes.
18751      * @param {Number} startIndex
18752      * @param {Number} endIndex
18753      * @return {Array} An array of nodes
18754      */
18755     getNodes : function(start, end){
18756         var ns = this.nodes;
18757         start = start || 0;
18758         end = typeof end == "undefined" ? ns.length - 1 : end;
18759         var nodes = [];
18760         if(start <= end){
18761             for(var i = start; i <= end; i++){
18762                 nodes.push(ns[i]);
18763             }
18764         } else{
18765             for(var i = start; i >= end; i--){
18766                 nodes.push(ns[i]);
18767             }
18768         }
18769         return nodes;
18770     },
18771
18772     /**
18773      * Finds the index of the passed node
18774      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18775      * @return {Number} The index of the node or -1
18776      */
18777     indexOf : function(node){
18778         node = this.getNode(node);
18779         if(typeof node.nodeIndex == "number"){
18780             return node.nodeIndex;
18781         }
18782         var ns = this.nodes;
18783         for(var i = 0, len = ns.length; i < len; i++){
18784             if(ns[i] == node){
18785                 return i;
18786             }
18787         }
18788         return -1;
18789     }
18790 });
18791 /*
18792  * - LGPL
18793  *
18794  * based on jquery fullcalendar
18795  * 
18796  */
18797
18798 Roo.bootstrap = Roo.bootstrap || {};
18799 /**
18800  * @class Roo.bootstrap.Calendar
18801  * @extends Roo.bootstrap.Component
18802  * Bootstrap Calendar class
18803  * @cfg {Boolean} loadMask (true|false) default false
18804  * @cfg {Object} header generate the user specific header of the calendar, default false
18805
18806  * @constructor
18807  * Create a new Container
18808  * @param {Object} config The config object
18809  */
18810
18811
18812
18813 Roo.bootstrap.Calendar = function(config){
18814     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18815      this.addEvents({
18816         /**
18817              * @event select
18818              * Fires when a date is selected
18819              * @param {DatePicker} this
18820              * @param {Date} date The selected date
18821              */
18822         'select': true,
18823         /**
18824              * @event monthchange
18825              * Fires when the displayed month changes 
18826              * @param {DatePicker} this
18827              * @param {Date} date The selected month
18828              */
18829         'monthchange': true,
18830         /**
18831              * @event evententer
18832              * Fires when mouse over an event
18833              * @param {Calendar} this
18834              * @param {event} Event
18835              */
18836         'evententer': true,
18837         /**
18838              * @event eventleave
18839              * Fires when the mouse leaves an
18840              * @param {Calendar} this
18841              * @param {event}
18842              */
18843         'eventleave': true,
18844         /**
18845              * @event eventclick
18846              * Fires when the mouse click an
18847              * @param {Calendar} this
18848              * @param {event}
18849              */
18850         'eventclick': true
18851         
18852     });
18853
18854 };
18855
18856 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18857     
18858      /**
18859      * @cfg {Number} startDay
18860      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18861      */
18862     startDay : 0,
18863     
18864     loadMask : false,
18865     
18866     header : false,
18867       
18868     getAutoCreate : function(){
18869         
18870         
18871         var fc_button = function(name, corner, style, content ) {
18872             return Roo.apply({},{
18873                 tag : 'span',
18874                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18875                          (corner.length ?
18876                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18877                             ''
18878                         ),
18879                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18880                 unselectable: 'on'
18881             });
18882         };
18883         
18884         var header = {};
18885         
18886         if(!this.header){
18887             header = {
18888                 tag : 'table',
18889                 cls : 'fc-header',
18890                 style : 'width:100%',
18891                 cn : [
18892                     {
18893                         tag: 'tr',
18894                         cn : [
18895                             {
18896                                 tag : 'td',
18897                                 cls : 'fc-header-left',
18898                                 cn : [
18899                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18900                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18901                                     { tag: 'span', cls: 'fc-header-space' },
18902                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18903
18904
18905                                 ]
18906                             },
18907
18908                             {
18909                                 tag : 'td',
18910                                 cls : 'fc-header-center',
18911                                 cn : [
18912                                     {
18913                                         tag: 'span',
18914                                         cls: 'fc-header-title',
18915                                         cn : {
18916                                             tag: 'H2',
18917                                             html : 'month / year'
18918                                         }
18919                                     }
18920
18921                                 ]
18922                             },
18923                             {
18924                                 tag : 'td',
18925                                 cls : 'fc-header-right',
18926                                 cn : [
18927                               /*      fc_button('month', 'left', '', 'month' ),
18928                                     fc_button('week', '', '', 'week' ),
18929                                     fc_button('day', 'right', '', 'day' )
18930                                 */    
18931
18932                                 ]
18933                             }
18934
18935                         ]
18936                     }
18937                 ]
18938             };
18939         }
18940         
18941         header = this.header;
18942         
18943        
18944         var cal_heads = function() {
18945             var ret = [];
18946             // fixme - handle this.
18947             
18948             for (var i =0; i < Date.dayNames.length; i++) {
18949                 var d = Date.dayNames[i];
18950                 ret.push({
18951                     tag: 'th',
18952                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18953                     html : d.substring(0,3)
18954                 });
18955                 
18956             }
18957             ret[0].cls += ' fc-first';
18958             ret[6].cls += ' fc-last';
18959             return ret;
18960         };
18961         var cal_cell = function(n) {
18962             return  {
18963                 tag: 'td',
18964                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18965                 cn : [
18966                     {
18967                         cn : [
18968                             {
18969                                 cls: 'fc-day-number',
18970                                 html: 'D'
18971                             },
18972                             {
18973                                 cls: 'fc-day-content',
18974                              
18975                                 cn : [
18976                                      {
18977                                         style: 'position: relative;' // height: 17px;
18978                                     }
18979                                 ]
18980                             }
18981                             
18982                             
18983                         ]
18984                     }
18985                 ]
18986                 
18987             }
18988         };
18989         var cal_rows = function() {
18990             
18991             var ret = [];
18992             for (var r = 0; r < 6; r++) {
18993                 var row= {
18994                     tag : 'tr',
18995                     cls : 'fc-week',
18996                     cn : []
18997                 };
18998                 
18999                 for (var i =0; i < Date.dayNames.length; i++) {
19000                     var d = Date.dayNames[i];
19001                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19002
19003                 }
19004                 row.cn[0].cls+=' fc-first';
19005                 row.cn[0].cn[0].style = 'min-height:90px';
19006                 row.cn[6].cls+=' fc-last';
19007                 ret.push(row);
19008                 
19009             }
19010             ret[0].cls += ' fc-first';
19011             ret[4].cls += ' fc-prev-last';
19012             ret[5].cls += ' fc-last';
19013             return ret;
19014             
19015         };
19016         
19017         var cal_table = {
19018             tag: 'table',
19019             cls: 'fc-border-separate',
19020             style : 'width:100%',
19021             cellspacing  : 0,
19022             cn : [
19023                 { 
19024                     tag: 'thead',
19025                     cn : [
19026                         { 
19027                             tag: 'tr',
19028                             cls : 'fc-first fc-last',
19029                             cn : cal_heads()
19030                         }
19031                     ]
19032                 },
19033                 { 
19034                     tag: 'tbody',
19035                     cn : cal_rows()
19036                 }
19037                   
19038             ]
19039         };
19040          
19041          var cfg = {
19042             cls : 'fc fc-ltr',
19043             cn : [
19044                 header,
19045                 {
19046                     cls : 'fc-content',
19047                     style : "position: relative;",
19048                     cn : [
19049                         {
19050                             cls : 'fc-view fc-view-month fc-grid',
19051                             style : 'position: relative',
19052                             unselectable : 'on',
19053                             cn : [
19054                                 {
19055                                     cls : 'fc-event-container',
19056                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19057                                 },
19058                                 cal_table
19059                             ]
19060                         }
19061                     ]
19062     
19063                 }
19064            ] 
19065             
19066         };
19067         
19068          
19069         
19070         return cfg;
19071     },
19072     
19073     
19074     initEvents : function()
19075     {
19076         if(!this.store){
19077             throw "can not find store for calendar";
19078         }
19079         
19080         var mark = {
19081             tag: "div",
19082             cls:"x-dlg-mask",
19083             style: "text-align:center",
19084             cn: [
19085                 {
19086                     tag: "div",
19087                     style: "background-color:white;width:50%;margin:250 auto",
19088                     cn: [
19089                         {
19090                             tag: "img",
19091                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19092                         },
19093                         {
19094                             tag: "span",
19095                             html: "Loading"
19096                         }
19097                         
19098                     ]
19099                 }
19100             ]
19101         };
19102         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19103         
19104         var size = this.el.select('.fc-content', true).first().getSize();
19105         this.maskEl.setSize(size.width, size.height);
19106         this.maskEl.enableDisplayMode("block");
19107         if(!this.loadMask){
19108             this.maskEl.hide();
19109         }
19110         
19111         this.store = Roo.factory(this.store, Roo.data);
19112         this.store.on('load', this.onLoad, this);
19113         this.store.on('beforeload', this.onBeforeLoad, this);
19114         
19115         this.resize();
19116         
19117         this.cells = this.el.select('.fc-day',true);
19118         //Roo.log(this.cells);
19119         this.textNodes = this.el.query('.fc-day-number');
19120         this.cells.addClassOnOver('fc-state-hover');
19121         
19122         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19123         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19124         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19125         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19126         
19127         this.on('monthchange', this.onMonthChange, this);
19128         
19129         this.update(new Date().clearTime());
19130     },
19131     
19132     resize : function() {
19133         var sz  = this.el.getSize();
19134         
19135         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19136         this.el.select('.fc-day-content div',true).setHeight(34);
19137     },
19138     
19139     
19140     // private
19141     showPrevMonth : function(e){
19142         this.update(this.activeDate.add("mo", -1));
19143     },
19144     showToday : function(e){
19145         this.update(new Date().clearTime());
19146     },
19147     // private
19148     showNextMonth : function(e){
19149         this.update(this.activeDate.add("mo", 1));
19150     },
19151
19152     // private
19153     showPrevYear : function(){
19154         this.update(this.activeDate.add("y", -1));
19155     },
19156
19157     // private
19158     showNextYear : function(){
19159         this.update(this.activeDate.add("y", 1));
19160     },
19161
19162     
19163    // private
19164     update : function(date)
19165     {
19166         var vd = this.activeDate;
19167         this.activeDate = date;
19168 //        if(vd && this.el){
19169 //            var t = date.getTime();
19170 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19171 //                Roo.log('using add remove');
19172 //                
19173 //                this.fireEvent('monthchange', this, date);
19174 //                
19175 //                this.cells.removeClass("fc-state-highlight");
19176 //                this.cells.each(function(c){
19177 //                   if(c.dateValue == t){
19178 //                       c.addClass("fc-state-highlight");
19179 //                       setTimeout(function(){
19180 //                            try{c.dom.firstChild.focus();}catch(e){}
19181 //                       }, 50);
19182 //                       return false;
19183 //                   }
19184 //                   return true;
19185 //                });
19186 //                return;
19187 //            }
19188 //        }
19189         
19190         var days = date.getDaysInMonth();
19191         
19192         var firstOfMonth = date.getFirstDateOfMonth();
19193         var startingPos = firstOfMonth.getDay()-this.startDay;
19194         
19195         if(startingPos < this.startDay){
19196             startingPos += 7;
19197         }
19198         
19199         var pm = date.add(Date.MONTH, -1);
19200         var prevStart = pm.getDaysInMonth()-startingPos;
19201 //        
19202         this.cells = this.el.select('.fc-day',true);
19203         this.textNodes = this.el.query('.fc-day-number');
19204         this.cells.addClassOnOver('fc-state-hover');
19205         
19206         var cells = this.cells.elements;
19207         var textEls = this.textNodes;
19208         
19209         Roo.each(cells, function(cell){
19210             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19211         });
19212         
19213         days += startingPos;
19214
19215         // convert everything to numbers so it's fast
19216         var day = 86400000;
19217         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19218         //Roo.log(d);
19219         //Roo.log(pm);
19220         //Roo.log(prevStart);
19221         
19222         var today = new Date().clearTime().getTime();
19223         var sel = date.clearTime().getTime();
19224         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19225         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19226         var ddMatch = this.disabledDatesRE;
19227         var ddText = this.disabledDatesText;
19228         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19229         var ddaysText = this.disabledDaysText;
19230         var format = this.format;
19231         
19232         var setCellClass = function(cal, cell){
19233             cell.row = 0;
19234             cell.events = [];
19235             cell.more = [];
19236             //Roo.log('set Cell Class');
19237             cell.title = "";
19238             var t = d.getTime();
19239             
19240             //Roo.log(d);
19241             
19242             cell.dateValue = t;
19243             if(t == today){
19244                 cell.className += " fc-today";
19245                 cell.className += " fc-state-highlight";
19246                 cell.title = cal.todayText;
19247             }
19248             if(t == sel){
19249                 // disable highlight in other month..
19250                 //cell.className += " fc-state-highlight";
19251                 
19252             }
19253             // disabling
19254             if(t < min) {
19255                 cell.className = " fc-state-disabled";
19256                 cell.title = cal.minText;
19257                 return;
19258             }
19259             if(t > max) {
19260                 cell.className = " fc-state-disabled";
19261                 cell.title = cal.maxText;
19262                 return;
19263             }
19264             if(ddays){
19265                 if(ddays.indexOf(d.getDay()) != -1){
19266                     cell.title = ddaysText;
19267                     cell.className = " fc-state-disabled";
19268                 }
19269             }
19270             if(ddMatch && format){
19271                 var fvalue = d.dateFormat(format);
19272                 if(ddMatch.test(fvalue)){
19273                     cell.title = ddText.replace("%0", fvalue);
19274                     cell.className = " fc-state-disabled";
19275                 }
19276             }
19277             
19278             if (!cell.initialClassName) {
19279                 cell.initialClassName = cell.dom.className;
19280             }
19281             
19282             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19283         };
19284
19285         var i = 0;
19286         
19287         for(; i < startingPos; i++) {
19288             textEls[i].innerHTML = (++prevStart);
19289             d.setDate(d.getDate()+1);
19290             
19291             cells[i].className = "fc-past fc-other-month";
19292             setCellClass(this, cells[i]);
19293         }
19294         
19295         var intDay = 0;
19296         
19297         for(; i < days; i++){
19298             intDay = i - startingPos + 1;
19299             textEls[i].innerHTML = (intDay);
19300             d.setDate(d.getDate()+1);
19301             
19302             cells[i].className = ''; // "x-date-active";
19303             setCellClass(this, cells[i]);
19304         }
19305         var extraDays = 0;
19306         
19307         for(; i < 42; i++) {
19308             textEls[i].innerHTML = (++extraDays);
19309             d.setDate(d.getDate()+1);
19310             
19311             cells[i].className = "fc-future fc-other-month";
19312             setCellClass(this, cells[i]);
19313         }
19314         
19315         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19316         
19317         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19318         
19319         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19320         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19321         
19322         if(totalRows != 6){
19323             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19324             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19325         }
19326         
19327         this.fireEvent('monthchange', this, date);
19328         
19329         
19330         /*
19331         if(!this.internalRender){
19332             var main = this.el.dom.firstChild;
19333             var w = main.offsetWidth;
19334             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19335             Roo.fly(main).setWidth(w);
19336             this.internalRender = true;
19337             // opera does not respect the auto grow header center column
19338             // then, after it gets a width opera refuses to recalculate
19339             // without a second pass
19340             if(Roo.isOpera && !this.secondPass){
19341                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19342                 this.secondPass = true;
19343                 this.update.defer(10, this, [date]);
19344             }
19345         }
19346         */
19347         
19348     },
19349     
19350     findCell : function(dt) {
19351         dt = dt.clearTime().getTime();
19352         var ret = false;
19353         this.cells.each(function(c){
19354             //Roo.log("check " +c.dateValue + '?=' + dt);
19355             if(c.dateValue == dt){
19356                 ret = c;
19357                 return false;
19358             }
19359             return true;
19360         });
19361         
19362         return ret;
19363     },
19364     
19365     findCells : function(ev) {
19366         var s = ev.start.clone().clearTime().getTime();
19367        // Roo.log(s);
19368         var e= ev.end.clone().clearTime().getTime();
19369        // Roo.log(e);
19370         var ret = [];
19371         this.cells.each(function(c){
19372              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19373             
19374             if(c.dateValue > e){
19375                 return ;
19376             }
19377             if(c.dateValue < s){
19378                 return ;
19379             }
19380             ret.push(c);
19381         });
19382         
19383         return ret;    
19384     },
19385     
19386 //    findBestRow: function(cells)
19387 //    {
19388 //        var ret = 0;
19389 //        
19390 //        for (var i =0 ; i < cells.length;i++) {
19391 //            ret  = Math.max(cells[i].rows || 0,ret);
19392 //        }
19393 //        return ret;
19394 //        
19395 //    },
19396     
19397     
19398     addItem : function(ev)
19399     {
19400         // look for vertical location slot in
19401         var cells = this.findCells(ev);
19402         
19403 //        ev.row = this.findBestRow(cells);
19404         
19405         // work out the location.
19406         
19407         var crow = false;
19408         var rows = [];
19409         for(var i =0; i < cells.length; i++) {
19410             
19411             cells[i].row = cells[0].row;
19412             
19413             if(i == 0){
19414                 cells[i].row = cells[i].row + 1;
19415             }
19416             
19417             if (!crow) {
19418                 crow = {
19419                     start : cells[i],
19420                     end :  cells[i]
19421                 };
19422                 continue;
19423             }
19424             if (crow.start.getY() == cells[i].getY()) {
19425                 // on same row.
19426                 crow.end = cells[i];
19427                 continue;
19428             }
19429             // different row.
19430             rows.push(crow);
19431             crow = {
19432                 start: cells[i],
19433                 end : cells[i]
19434             };
19435             
19436         }
19437         
19438         rows.push(crow);
19439         ev.els = [];
19440         ev.rows = rows;
19441         ev.cells = cells;
19442         
19443         cells[0].events.push(ev);
19444         
19445         this.calevents.push(ev);
19446     },
19447     
19448     clearEvents: function() {
19449         
19450         if(!this.calevents){
19451             return;
19452         }
19453         
19454         Roo.each(this.cells.elements, function(c){
19455             c.row = 0;
19456             c.events = [];
19457             c.more = [];
19458         });
19459         
19460         Roo.each(this.calevents, function(e) {
19461             Roo.each(e.els, function(el) {
19462                 el.un('mouseenter' ,this.onEventEnter, this);
19463                 el.un('mouseleave' ,this.onEventLeave, this);
19464                 el.remove();
19465             },this);
19466         },this);
19467         
19468         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19469             e.remove();
19470         });
19471         
19472     },
19473     
19474     renderEvents: function()
19475     {   
19476         var _this = this;
19477         
19478         this.cells.each(function(c) {
19479             
19480             if(c.row < 5){
19481                 return;
19482             }
19483             
19484             var ev = c.events;
19485             
19486             var r = 4;
19487             if(c.row != c.events.length){
19488                 r = 4 - (4 - (c.row - c.events.length));
19489             }
19490             
19491             c.events = ev.slice(0, r);
19492             c.more = ev.slice(r);
19493             
19494             if(c.more.length && c.more.length == 1){
19495                 c.events.push(c.more.pop());
19496             }
19497             
19498             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19499             
19500         });
19501             
19502         this.cells.each(function(c) {
19503             
19504             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19505             
19506             
19507             for (var e = 0; e < c.events.length; e++){
19508                 var ev = c.events[e];
19509                 var rows = ev.rows;
19510                 
19511                 for(var i = 0; i < rows.length; i++) {
19512                 
19513                     // how many rows should it span..
19514
19515                     var  cfg = {
19516                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19517                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19518
19519                         unselectable : "on",
19520                         cn : [
19521                             {
19522                                 cls: 'fc-event-inner',
19523                                 cn : [
19524     //                                {
19525     //                                  tag:'span',
19526     //                                  cls: 'fc-event-time',
19527     //                                  html : cells.length > 1 ? '' : ev.time
19528     //                                },
19529                                     {
19530                                       tag:'span',
19531                                       cls: 'fc-event-title',
19532                                       html : String.format('{0}', ev.title)
19533                                     }
19534
19535
19536                                 ]
19537                             },
19538                             {
19539                                 cls: 'ui-resizable-handle ui-resizable-e',
19540                                 html : '&nbsp;&nbsp;&nbsp'
19541                             }
19542
19543                         ]
19544                     };
19545
19546                     if (i == 0) {
19547                         cfg.cls += ' fc-event-start';
19548                     }
19549                     if ((i+1) == rows.length) {
19550                         cfg.cls += ' fc-event-end';
19551                     }
19552
19553                     var ctr = _this.el.select('.fc-event-container',true).first();
19554                     var cg = ctr.createChild(cfg);
19555
19556                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19557                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19558
19559                     var r = (c.more.length) ? 1 : 0;
19560                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19561                     cg.setWidth(ebox.right - sbox.x -2);
19562
19563                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19564                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19565                     cg.on('click', _this.onEventClick, _this, ev);
19566
19567                     ev.els.push(cg);
19568                     
19569                 }
19570                 
19571             }
19572             
19573             
19574             if(c.more.length){
19575                 var  cfg = {
19576                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19577                     style : 'position: absolute',
19578                     unselectable : "on",
19579                     cn : [
19580                         {
19581                             cls: 'fc-event-inner',
19582                             cn : [
19583                                 {
19584                                   tag:'span',
19585                                   cls: 'fc-event-title',
19586                                   html : 'More'
19587                                 }
19588
19589
19590                             ]
19591                         },
19592                         {
19593                             cls: 'ui-resizable-handle ui-resizable-e',
19594                             html : '&nbsp;&nbsp;&nbsp'
19595                         }
19596
19597                     ]
19598                 };
19599
19600                 var ctr = _this.el.select('.fc-event-container',true).first();
19601                 var cg = ctr.createChild(cfg);
19602
19603                 var sbox = c.select('.fc-day-content',true).first().getBox();
19604                 var ebox = c.select('.fc-day-content',true).first().getBox();
19605                 //Roo.log(cg);
19606                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19607                 cg.setWidth(ebox.right - sbox.x -2);
19608
19609                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19610                 
19611             }
19612             
19613         });
19614         
19615         
19616         
19617     },
19618     
19619     onEventEnter: function (e, el,event,d) {
19620         this.fireEvent('evententer', this, el, event);
19621     },
19622     
19623     onEventLeave: function (e, el,event,d) {
19624         this.fireEvent('eventleave', this, el, event);
19625     },
19626     
19627     onEventClick: function (e, el,event,d) {
19628         this.fireEvent('eventclick', this, el, event);
19629     },
19630     
19631     onMonthChange: function () {
19632         this.store.load();
19633     },
19634     
19635     onMoreEventClick: function(e, el, more)
19636     {
19637         var _this = this;
19638         
19639         this.calpopover.placement = 'right';
19640         this.calpopover.setTitle('More');
19641         
19642         this.calpopover.setContent('');
19643         
19644         var ctr = this.calpopover.el.select('.popover-content', true).first();
19645         
19646         Roo.each(more, function(m){
19647             var cfg = {
19648                 cls : 'fc-event-hori fc-event-draggable',
19649                 html : m.title
19650             };
19651             var cg = ctr.createChild(cfg);
19652             
19653             cg.on('click', _this.onEventClick, _this, m);
19654         });
19655         
19656         this.calpopover.show(el);
19657         
19658         
19659     },
19660     
19661     onLoad: function () 
19662     {   
19663         this.calevents = [];
19664         var cal = this;
19665         
19666         if(this.store.getCount() > 0){
19667             this.store.data.each(function(d){
19668                cal.addItem({
19669                     id : d.data.id,
19670                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19671                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19672                     time : d.data.start_time,
19673                     title : d.data.title,
19674                     description : d.data.description,
19675                     venue : d.data.venue
19676                 });
19677             });
19678         }
19679         
19680         this.renderEvents();
19681         
19682         if(this.calevents.length && this.loadMask){
19683             this.maskEl.hide();
19684         }
19685     },
19686     
19687     onBeforeLoad: function()
19688     {
19689         this.clearEvents();
19690         if(this.loadMask){
19691             this.maskEl.show();
19692         }
19693     }
19694 });
19695
19696  
19697  /*
19698  * - LGPL
19699  *
19700  * element
19701  * 
19702  */
19703
19704 /**
19705  * @class Roo.bootstrap.Popover
19706  * @extends Roo.bootstrap.Component
19707  * Bootstrap Popover class
19708  * @cfg {String} html contents of the popover   (or false to use children..)
19709  * @cfg {String} title of popover (or false to hide)
19710  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19711  * @cfg {String} trigger click || hover (or false to trigger manually)
19712  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19713  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19714  *      - if false and it has a 'parent' then it will be automatically added to that element
19715  *      - if string - Roo.get  will be called 
19716  * @cfg {Number} delay - delay before showing
19717  
19718  * @constructor
19719  * Create a new Popover
19720  * @param {Object} config The config object
19721  */
19722
19723 Roo.bootstrap.Popover = function(config){
19724     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19725     
19726     this.addEvents({
19727         // raw events
19728          /**
19729          * @event show
19730          * After the popover show
19731          * 
19732          * @param {Roo.bootstrap.Popover} this
19733          */
19734         "show" : true,
19735         /**
19736          * @event hide
19737          * After the popover hide
19738          * 
19739          * @param {Roo.bootstrap.Popover} this
19740          */
19741         "hide" : true
19742     });
19743 };
19744
19745 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19746     
19747     title: false,
19748     html: false,
19749     
19750     placement : 'right',
19751     trigger : 'hover', // hover
19752     modal : false,
19753     delay : 0,
19754     
19755     over: false,
19756     
19757     can_build_overlaid : false,
19758     
19759     maskEl : false, // the mask element
19760     headerEl : false,
19761     contentEl : false,
19762     alignEl : false, // when show is called with an element - this get's stored.
19763     
19764     getChildContainer : function()
19765     {
19766         return this.contentEl;
19767         
19768     },
19769     getPopoverHeader : function()
19770     {
19771         this.title = true; // flag not to hide it..
19772         this.headerEl.addClass('p-0');
19773         return this.headerEl
19774     },
19775     
19776     
19777     getAutoCreate : function(){
19778          
19779         var cfg = {
19780            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19781            style: 'display:block',
19782            cn : [
19783                 {
19784                     cls : 'arrow'
19785                 },
19786                 {
19787                     cls : 'popover-inner ',
19788                     cn : [
19789                         {
19790                             tag: 'h3',
19791                             cls: 'popover-title popover-header',
19792                             html : this.title === false ? '' : this.title
19793                         },
19794                         {
19795                             cls : 'popover-content popover-body '  + (this.cls || ''),
19796                             html : this.html || ''
19797                         }
19798                     ]
19799                     
19800                 }
19801            ]
19802         };
19803         
19804         return cfg;
19805     },
19806     /**
19807      * @param {string} the title
19808      */
19809     setTitle: function(str)
19810     {
19811         this.title = str;
19812         if (this.el) {
19813             this.headerEl.dom.innerHTML = str;
19814         }
19815         
19816     },
19817     /**
19818      * @param {string} the body content
19819      */
19820     setContent: function(str)
19821     {
19822         this.html = str;
19823         if (this.contentEl) {
19824             this.contentEl.dom.innerHTML = str;
19825         }
19826         
19827     },
19828     // as it get's added to the bottom of the page.
19829     onRender : function(ct, position)
19830     {
19831         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19832         
19833         
19834         
19835         if(!this.el){
19836             var cfg = Roo.apply({},  this.getAutoCreate());
19837             cfg.id = Roo.id();
19838             
19839             if (this.cls) {
19840                 cfg.cls += ' ' + this.cls;
19841             }
19842             if (this.style) {
19843                 cfg.style = this.style;
19844             }
19845             //Roo.log("adding to ");
19846             this.el = Roo.get(document.body).createChild(cfg, position);
19847 //            Roo.log(this.el);
19848         }
19849         
19850         this.contentEl = this.el.select('.popover-content',true).first();
19851         this.headerEl =  this.el.select('.popover-title',true).first();
19852         
19853         var nitems = [];
19854         if(typeof(this.items) != 'undefined'){
19855             var items = this.items;
19856             delete this.items;
19857
19858             for(var i =0;i < items.length;i++) {
19859                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19860             }
19861         }
19862
19863         this.items = nitems;
19864         
19865         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19866         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19867         
19868         
19869         
19870         this.initEvents();
19871     },
19872     
19873     resizeMask : function()
19874     {
19875         this.maskEl.setSize(
19876             Roo.lib.Dom.getViewWidth(true),
19877             Roo.lib.Dom.getViewHeight(true)
19878         );
19879     },
19880     
19881     initEvents : function()
19882     {
19883         
19884         if (!this.modal) { 
19885             Roo.bootstrap.Popover.register(this);
19886         }
19887          
19888         this.arrowEl = this.el.select('.arrow',true).first();
19889         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19890         this.el.enableDisplayMode('block');
19891         this.el.hide();
19892  
19893         
19894         if (this.over === false && !this.parent()) {
19895             return; 
19896         }
19897         if (this.triggers === false) {
19898             return;
19899         }
19900          
19901         // support parent
19902         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19903         var triggers = this.trigger ? this.trigger.split(' ') : [];
19904         Roo.each(triggers, function(trigger) {
19905         
19906             if (trigger == 'click') {
19907                 on_el.on('click', this.toggle, this);
19908             } else if (trigger != 'manual') {
19909                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19910                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19911       
19912                 on_el.on(eventIn  ,this.enter, this);
19913                 on_el.on(eventOut, this.leave, this);
19914             }
19915         }, this);
19916     },
19917     
19918     
19919     // private
19920     timeout : null,
19921     hoverState : null,
19922     
19923     toggle : function () {
19924         this.hoverState == 'in' ? this.leave() : this.enter();
19925     },
19926     
19927     enter : function () {
19928         
19929         clearTimeout(this.timeout);
19930     
19931         this.hoverState = 'in';
19932     
19933         if (!this.delay || !this.delay.show) {
19934             this.show();
19935             return;
19936         }
19937         var _t = this;
19938         this.timeout = setTimeout(function () {
19939             if (_t.hoverState == 'in') {
19940                 _t.show();
19941             }
19942         }, this.delay.show)
19943     },
19944     
19945     leave : function() {
19946         clearTimeout(this.timeout);
19947     
19948         this.hoverState = 'out';
19949     
19950         if (!this.delay || !this.delay.hide) {
19951             this.hide();
19952             return;
19953         }
19954         var _t = this;
19955         this.timeout = setTimeout(function () {
19956             if (_t.hoverState == 'out') {
19957                 _t.hide();
19958             }
19959         }, this.delay.hide)
19960     },
19961     /**
19962      * Show the popover
19963      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19964      * @param {string} (left|right|top|bottom) position
19965      */
19966     show : function (on_el, placement)
19967     {
19968         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19969         on_el = on_el || false; // default to false
19970          
19971         if (!on_el) {
19972             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19973                 on_el = this.parent().el;
19974             } else if (this.over) {
19975                 Roo.get(this.over);
19976             }
19977             
19978         }
19979         
19980         this.alignEl = Roo.get( on_el );
19981
19982         if (!this.el) {
19983             this.render(document.body);
19984         }
19985         
19986         
19987          
19988         
19989         if (this.title === false) {
19990             this.headerEl.hide();
19991         }
19992         
19993        
19994         this.el.show();
19995         this.el.dom.style.display = 'block';
19996          
19997  
19998         if (this.alignEl) {
19999             this.updatePosition(this.placement, true);
20000              
20001         } else {
20002             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20003             var es = this.el.getSize();
20004             var x = Roo.lib.Dom.getViewWidth()/2;
20005             var y = Roo.lib.Dom.getViewHeight()/2;
20006             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20007             
20008         }
20009
20010         
20011         //var arrow = this.el.select('.arrow',true).first();
20012         //arrow.set(align[2], 
20013         
20014         this.el.addClass('in');
20015         
20016          
20017         
20018         this.hoverState = 'in';
20019         
20020         if (this.modal) {
20021             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20022             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20023             this.maskEl.dom.style.display = 'block';
20024             this.maskEl.addClass('show');
20025         }
20026         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20027  
20028         this.fireEvent('show', this);
20029         
20030     },
20031     /**
20032      * fire this manually after loading a grid in the table for example
20033      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20034      * @param {Boolean} try and move it if we cant get right position.
20035      */
20036     updatePosition : function(placement, try_move)
20037     {
20038         // allow for calling with no parameters
20039         placement = placement   ? placement :  this.placement;
20040         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20041         
20042         this.el.removeClass([
20043             'fade','top','bottom', 'left', 'right','in',
20044             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20045         ]);
20046         this.el.addClass(placement + ' bs-popover-' + placement);
20047         
20048         if (!this.alignEl ) {
20049             return false;
20050         }
20051         
20052         switch (placement) {
20053             case 'right':
20054                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20055                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20056                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20057                     //normal display... or moved up/down.
20058                     this.el.setXY(offset);
20059                     var xy = this.alignEl.getAnchorXY('tr', false);
20060                     xy[0]+=2;xy[1]+=5;
20061                     this.arrowEl.setXY(xy);
20062                     return true;
20063                 }
20064                 // continue through...
20065                 return this.updatePosition('left', false);
20066                 
20067             
20068             case 'left':
20069                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20070                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20071                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20072                     //normal display... or moved up/down.
20073                     this.el.setXY(offset);
20074                     var xy = this.alignEl.getAnchorXY('tl', false);
20075                     xy[0]-=10;xy[1]+=5; // << fix me
20076                     this.arrowEl.setXY(xy);
20077                     return true;
20078                 }
20079                 // call self...
20080                 return this.updatePosition('right', false);
20081             
20082             case 'top':
20083                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20084                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20085                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20086                     //normal display... or moved up/down.
20087                     this.el.setXY(offset);
20088                     var xy = this.alignEl.getAnchorXY('t', false);
20089                     xy[1]-=10; // << fix me
20090                     this.arrowEl.setXY(xy);
20091                     return true;
20092                 }
20093                 // fall through
20094                return this.updatePosition('bottom', false);
20095             
20096             case 'bottom':
20097                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20098                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20099                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20100                     //normal display... or moved up/down.
20101                     this.el.setXY(offset);
20102                     var xy = this.alignEl.getAnchorXY('b', false);
20103                      xy[1]+=2; // << fix me
20104                     this.arrowEl.setXY(xy);
20105                     return true;
20106                 }
20107                 // fall through
20108                 return this.updatePosition('top', false);
20109                 
20110             
20111         }
20112         
20113         
20114         return false;
20115     },
20116     
20117     hide : function()
20118     {
20119         this.el.setXY([0,0]);
20120         this.el.removeClass('in');
20121         this.el.hide();
20122         this.hoverState = null;
20123         this.maskEl.hide(); // always..
20124         this.fireEvent('hide', this);
20125     }
20126     
20127 });
20128
20129
20130 Roo.apply(Roo.bootstrap.Popover, {
20131
20132     alignment : {
20133         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20134         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20135         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20136         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20137     },
20138     
20139     zIndex : 20001,
20140
20141     clickHander : false,
20142     
20143
20144     onMouseDown : function(e)
20145     {
20146         if (!e.getTarget(".roo-popover")) {
20147             this.hideAll();
20148         }
20149          
20150     },
20151     
20152     popups : [],
20153     
20154     register : function(popup)
20155     {
20156         if (!Roo.bootstrap.Popover.clickHandler) {
20157             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20158         }
20159         // hide other popups.
20160         this.hideAll();
20161         this.popups.push(popup);
20162     },
20163     hideAll : function()
20164     {
20165         this.popups.forEach(function(p) {
20166             p.hide();
20167         });
20168     }
20169
20170 });/*
20171  * - LGPL
20172  *
20173  * Card header - holder for the card header elements.
20174  * 
20175  */
20176
20177 /**
20178  * @class Roo.bootstrap.PopoverNav
20179  * @extends Roo.bootstrap.NavGroup
20180  * Bootstrap Popover header navigation class
20181  * @constructor
20182  * Create a new Popover Header Navigation 
20183  * @param {Object} config The config object
20184  */
20185
20186 Roo.bootstrap.PopoverNav = function(config){
20187     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20188 };
20189
20190 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20191     
20192     
20193     container_method : 'getPopoverHeader' 
20194     
20195      
20196     
20197     
20198    
20199 });
20200
20201  
20202
20203  /*
20204  * - LGPL
20205  *
20206  * Progress
20207  * 
20208  */
20209
20210 /**
20211  * @class Roo.bootstrap.Progress
20212  * @extends Roo.bootstrap.Component
20213  * Bootstrap Progress class
20214  * @cfg {Boolean} striped striped of the progress bar
20215  * @cfg {Boolean} active animated of the progress bar
20216  * 
20217  * 
20218  * @constructor
20219  * Create a new Progress
20220  * @param {Object} config The config object
20221  */
20222
20223 Roo.bootstrap.Progress = function(config){
20224     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20225 };
20226
20227 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20228     
20229     striped : false,
20230     active: false,
20231     
20232     getAutoCreate : function(){
20233         var cfg = {
20234             tag: 'div',
20235             cls: 'progress'
20236         };
20237         
20238         
20239         if(this.striped){
20240             cfg.cls += ' progress-striped';
20241         }
20242       
20243         if(this.active){
20244             cfg.cls += ' active';
20245         }
20246         
20247         
20248         return cfg;
20249     }
20250    
20251 });
20252
20253  
20254
20255  /*
20256  * - LGPL
20257  *
20258  * ProgressBar
20259  * 
20260  */
20261
20262 /**
20263  * @class Roo.bootstrap.ProgressBar
20264  * @extends Roo.bootstrap.Component
20265  * Bootstrap ProgressBar class
20266  * @cfg {Number} aria_valuenow aria-value now
20267  * @cfg {Number} aria_valuemin aria-value min
20268  * @cfg {Number} aria_valuemax aria-value max
20269  * @cfg {String} label label for the progress bar
20270  * @cfg {String} panel (success | info | warning | danger )
20271  * @cfg {String} role role of the progress bar
20272  * @cfg {String} sr_only text
20273  * 
20274  * 
20275  * @constructor
20276  * Create a new ProgressBar
20277  * @param {Object} config The config object
20278  */
20279
20280 Roo.bootstrap.ProgressBar = function(config){
20281     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20282 };
20283
20284 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20285     
20286     aria_valuenow : 0,
20287     aria_valuemin : 0,
20288     aria_valuemax : 100,
20289     label : false,
20290     panel : false,
20291     role : false,
20292     sr_only: false,
20293     
20294     getAutoCreate : function()
20295     {
20296         
20297         var cfg = {
20298             tag: 'div',
20299             cls: 'progress-bar',
20300             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20301         };
20302         
20303         if(this.sr_only){
20304             cfg.cn = {
20305                 tag: 'span',
20306                 cls: 'sr-only',
20307                 html: this.sr_only
20308             }
20309         }
20310         
20311         if(this.role){
20312             cfg.role = this.role;
20313         }
20314         
20315         if(this.aria_valuenow){
20316             cfg['aria-valuenow'] = this.aria_valuenow;
20317         }
20318         
20319         if(this.aria_valuemin){
20320             cfg['aria-valuemin'] = this.aria_valuemin;
20321         }
20322         
20323         if(this.aria_valuemax){
20324             cfg['aria-valuemax'] = this.aria_valuemax;
20325         }
20326         
20327         if(this.label && !this.sr_only){
20328             cfg.html = this.label;
20329         }
20330         
20331         if(this.panel){
20332             cfg.cls += ' progress-bar-' + this.panel;
20333         }
20334         
20335         return cfg;
20336     },
20337     
20338     update : function(aria_valuenow)
20339     {
20340         this.aria_valuenow = aria_valuenow;
20341         
20342         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20343     }
20344    
20345 });
20346
20347  
20348
20349  /*
20350  * - LGPL
20351  *
20352  * column
20353  * 
20354  */
20355
20356 /**
20357  * @class Roo.bootstrap.TabGroup
20358  * @extends Roo.bootstrap.Column
20359  * Bootstrap Column class
20360  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20361  * @cfg {Boolean} carousel true to make the group behave like a carousel
20362  * @cfg {Boolean} bullets show bullets for the panels
20363  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20364  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20365  * @cfg {Boolean} showarrow (true|false) show arrow default true
20366  * 
20367  * @constructor
20368  * Create a new TabGroup
20369  * @param {Object} config The config object
20370  */
20371
20372 Roo.bootstrap.TabGroup = function(config){
20373     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20374     if (!this.navId) {
20375         this.navId = Roo.id();
20376     }
20377     this.tabs = [];
20378     Roo.bootstrap.TabGroup.register(this);
20379     
20380 };
20381
20382 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20383     
20384     carousel : false,
20385     transition : false,
20386     bullets : 0,
20387     timer : 0,
20388     autoslide : false,
20389     slideFn : false,
20390     slideOnTouch : false,
20391     showarrow : true,
20392     
20393     getAutoCreate : function()
20394     {
20395         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20396         
20397         cfg.cls += ' tab-content';
20398         
20399         if (this.carousel) {
20400             cfg.cls += ' carousel slide';
20401             
20402             cfg.cn = [{
20403                cls : 'carousel-inner',
20404                cn : []
20405             }];
20406         
20407             if(this.bullets  && !Roo.isTouch){
20408                 
20409                 var bullets = {
20410                     cls : 'carousel-bullets',
20411                     cn : []
20412                 };
20413                
20414                 if(this.bullets_cls){
20415                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20416                 }
20417                 
20418                 bullets.cn.push({
20419                     cls : 'clear'
20420                 });
20421                 
20422                 cfg.cn[0].cn.push(bullets);
20423             }
20424             
20425             if(this.showarrow){
20426                 cfg.cn[0].cn.push({
20427                     tag : 'div',
20428                     class : 'carousel-arrow',
20429                     cn : [
20430                         {
20431                             tag : 'div',
20432                             class : 'carousel-prev',
20433                             cn : [
20434                                 {
20435                                     tag : 'i',
20436                                     class : 'fa fa-chevron-left'
20437                                 }
20438                             ]
20439                         },
20440                         {
20441                             tag : 'div',
20442                             class : 'carousel-next',
20443                             cn : [
20444                                 {
20445                                     tag : 'i',
20446                                     class : 'fa fa-chevron-right'
20447                                 }
20448                             ]
20449                         }
20450                     ]
20451                 });
20452             }
20453             
20454         }
20455         
20456         return cfg;
20457     },
20458     
20459     initEvents:  function()
20460     {
20461 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20462 //            this.el.on("touchstart", this.onTouchStart, this);
20463 //        }
20464         
20465         if(this.autoslide){
20466             var _this = this;
20467             
20468             this.slideFn = window.setInterval(function() {
20469                 _this.showPanelNext();
20470             }, this.timer);
20471         }
20472         
20473         if(this.showarrow){
20474             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20475             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20476         }
20477         
20478         
20479     },
20480     
20481 //    onTouchStart : function(e, el, o)
20482 //    {
20483 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20484 //            return;
20485 //        }
20486 //        
20487 //        this.showPanelNext();
20488 //    },
20489     
20490     
20491     getChildContainer : function()
20492     {
20493         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20494     },
20495     
20496     /**
20497     * register a Navigation item
20498     * @param {Roo.bootstrap.NavItem} the navitem to add
20499     */
20500     register : function(item)
20501     {
20502         this.tabs.push( item);
20503         item.navId = this.navId; // not really needed..
20504         this.addBullet();
20505     
20506     },
20507     
20508     getActivePanel : function()
20509     {
20510         var r = false;
20511         Roo.each(this.tabs, function(t) {
20512             if (t.active) {
20513                 r = t;
20514                 return false;
20515             }
20516             return null;
20517         });
20518         return r;
20519         
20520     },
20521     getPanelByName : function(n)
20522     {
20523         var r = false;
20524         Roo.each(this.tabs, function(t) {
20525             if (t.tabId == n) {
20526                 r = t;
20527                 return false;
20528             }
20529             return null;
20530         });
20531         return r;
20532     },
20533     indexOfPanel : function(p)
20534     {
20535         var r = false;
20536         Roo.each(this.tabs, function(t,i) {
20537             if (t.tabId == p.tabId) {
20538                 r = i;
20539                 return false;
20540             }
20541             return null;
20542         });
20543         return r;
20544     },
20545     /**
20546      * show a specific panel
20547      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20548      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20549      */
20550     showPanel : function (pan)
20551     {
20552         if(this.transition || typeof(pan) == 'undefined'){
20553             Roo.log("waiting for the transitionend");
20554             return false;
20555         }
20556         
20557         if (typeof(pan) == 'number') {
20558             pan = this.tabs[pan];
20559         }
20560         
20561         if (typeof(pan) == 'string') {
20562             pan = this.getPanelByName(pan);
20563         }
20564         
20565         var cur = this.getActivePanel();
20566         
20567         if(!pan || !cur){
20568             Roo.log('pan or acitve pan is undefined');
20569             return false;
20570         }
20571         
20572         if (pan.tabId == this.getActivePanel().tabId) {
20573             return true;
20574         }
20575         
20576         if (false === cur.fireEvent('beforedeactivate')) {
20577             return false;
20578         }
20579         
20580         if(this.bullets > 0 && !Roo.isTouch){
20581             this.setActiveBullet(this.indexOfPanel(pan));
20582         }
20583         
20584         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20585             
20586             //class="carousel-item carousel-item-next carousel-item-left"
20587             
20588             this.transition = true;
20589             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20590             var lr = dir == 'next' ? 'left' : 'right';
20591             pan.el.addClass(dir); // or prev
20592             pan.el.addClass('carousel-item-' + dir); // or prev
20593             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20594             cur.el.addClass(lr); // or right
20595             pan.el.addClass(lr);
20596             cur.el.addClass('carousel-item-' +lr); // or right
20597             pan.el.addClass('carousel-item-' +lr);
20598             
20599             
20600             var _this = this;
20601             cur.el.on('transitionend', function() {
20602                 Roo.log("trans end?");
20603                 
20604                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20605                 pan.setActive(true);
20606                 
20607                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20608                 cur.setActive(false);
20609                 
20610                 _this.transition = false;
20611                 
20612             }, this, { single:  true } );
20613             
20614             return true;
20615         }
20616         
20617         cur.setActive(false);
20618         pan.setActive(true);
20619         
20620         return true;
20621         
20622     },
20623     showPanelNext : function()
20624     {
20625         var i = this.indexOfPanel(this.getActivePanel());
20626         
20627         if (i >= this.tabs.length - 1 && !this.autoslide) {
20628             return;
20629         }
20630         
20631         if (i >= this.tabs.length - 1 && this.autoslide) {
20632             i = -1;
20633         }
20634         
20635         this.showPanel(this.tabs[i+1]);
20636     },
20637     
20638     showPanelPrev : function()
20639     {
20640         var i = this.indexOfPanel(this.getActivePanel());
20641         
20642         if (i  < 1 && !this.autoslide) {
20643             return;
20644         }
20645         
20646         if (i < 1 && this.autoslide) {
20647             i = this.tabs.length;
20648         }
20649         
20650         this.showPanel(this.tabs[i-1]);
20651     },
20652     
20653     
20654     addBullet: function()
20655     {
20656         if(!this.bullets || Roo.isTouch){
20657             return;
20658         }
20659         var ctr = this.el.select('.carousel-bullets',true).first();
20660         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20661         var bullet = ctr.createChild({
20662             cls : 'bullet bullet-' + i
20663         },ctr.dom.lastChild);
20664         
20665         
20666         var _this = this;
20667         
20668         bullet.on('click', (function(e, el, o, ii, t){
20669
20670             e.preventDefault();
20671
20672             this.showPanel(ii);
20673
20674             if(this.autoslide && this.slideFn){
20675                 clearInterval(this.slideFn);
20676                 this.slideFn = window.setInterval(function() {
20677                     _this.showPanelNext();
20678                 }, this.timer);
20679             }
20680
20681         }).createDelegate(this, [i, bullet], true));
20682                 
20683         
20684     },
20685      
20686     setActiveBullet : function(i)
20687     {
20688         if(Roo.isTouch){
20689             return;
20690         }
20691         
20692         Roo.each(this.el.select('.bullet', true).elements, function(el){
20693             el.removeClass('selected');
20694         });
20695
20696         var bullet = this.el.select('.bullet-' + i, true).first();
20697         
20698         if(!bullet){
20699             return;
20700         }
20701         
20702         bullet.addClass('selected');
20703     }
20704     
20705     
20706   
20707 });
20708
20709  
20710
20711  
20712  
20713 Roo.apply(Roo.bootstrap.TabGroup, {
20714     
20715     groups: {},
20716      /**
20717     * register a Navigation Group
20718     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20719     */
20720     register : function(navgrp)
20721     {
20722         this.groups[navgrp.navId] = navgrp;
20723         
20724     },
20725     /**
20726     * fetch a Navigation Group based on the navigation ID
20727     * if one does not exist , it will get created.
20728     * @param {string} the navgroup to add
20729     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20730     */
20731     get: function(navId) {
20732         if (typeof(this.groups[navId]) == 'undefined') {
20733             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20734         }
20735         return this.groups[navId] ;
20736     }
20737     
20738     
20739     
20740 });
20741
20742  /*
20743  * - LGPL
20744  *
20745  * TabPanel
20746  * 
20747  */
20748
20749 /**
20750  * @class Roo.bootstrap.TabPanel
20751  * @extends Roo.bootstrap.Component
20752  * Bootstrap TabPanel class
20753  * @cfg {Boolean} active panel active
20754  * @cfg {String} html panel content
20755  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20756  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20757  * @cfg {String} href click to link..
20758  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20759  * 
20760  * 
20761  * @constructor
20762  * Create a new TabPanel
20763  * @param {Object} config The config object
20764  */
20765
20766 Roo.bootstrap.TabPanel = function(config){
20767     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20768     this.addEvents({
20769         /**
20770              * @event changed
20771              * Fires when the active status changes
20772              * @param {Roo.bootstrap.TabPanel} this
20773              * @param {Boolean} state the new state
20774             
20775          */
20776         'changed': true,
20777         /**
20778              * @event beforedeactivate
20779              * Fires before a tab is de-activated - can be used to do validation on a form.
20780              * @param {Roo.bootstrap.TabPanel} this
20781              * @return {Boolean} false if there is an error
20782             
20783          */
20784         'beforedeactivate': true
20785      });
20786     
20787     this.tabId = this.tabId || Roo.id();
20788   
20789 };
20790
20791 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20792     
20793     active: false,
20794     html: false,
20795     tabId: false,
20796     navId : false,
20797     href : '',
20798     touchSlide : false,
20799     getAutoCreate : function(){
20800         
20801         
20802         var cfg = {
20803             tag: 'div',
20804             // item is needed for carousel - not sure if it has any effect otherwise
20805             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20806             html: this.html || ''
20807         };
20808         
20809         if(this.active){
20810             cfg.cls += ' active';
20811         }
20812         
20813         if(this.tabId){
20814             cfg.tabId = this.tabId;
20815         }
20816         
20817         
20818         
20819         return cfg;
20820     },
20821     
20822     initEvents:  function()
20823     {
20824         var p = this.parent();
20825         
20826         this.navId = this.navId || p.navId;
20827         
20828         if (typeof(this.navId) != 'undefined') {
20829             // not really needed.. but just in case.. parent should be a NavGroup.
20830             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20831             
20832             tg.register(this);
20833             
20834             var i = tg.tabs.length - 1;
20835             
20836             if(this.active && tg.bullets > 0 && i < tg.bullets){
20837                 tg.setActiveBullet(i);
20838             }
20839         }
20840         
20841         this.el.on('click', this.onClick, this);
20842         
20843         if(Roo.isTouch && this.touchSlide){
20844             this.el.on("touchstart", this.onTouchStart, this);
20845             this.el.on("touchmove", this.onTouchMove, this);
20846             this.el.on("touchend", this.onTouchEnd, this);
20847         }
20848         
20849     },
20850     
20851     onRender : function(ct, position)
20852     {
20853         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20854     },
20855     
20856     setActive : function(state)
20857     {
20858         Roo.log("panel - set active " + this.tabId + "=" + state);
20859         
20860         this.active = state;
20861         if (!state) {
20862             this.el.removeClass('active');
20863             
20864         } else  if (!this.el.hasClass('active')) {
20865             this.el.addClass('active');
20866         }
20867         
20868         this.fireEvent('changed', this, state);
20869     },
20870     
20871     onClick : function(e)
20872     {
20873         e.preventDefault();
20874         
20875         if(!this.href.length){
20876             return;
20877         }
20878         
20879         window.location.href = this.href;
20880     },
20881     
20882     startX : 0,
20883     startY : 0,
20884     endX : 0,
20885     endY : 0,
20886     swiping : false,
20887     
20888     onTouchStart : function(e)
20889     {
20890         this.swiping = false;
20891         
20892         this.startX = e.browserEvent.touches[0].clientX;
20893         this.startY = e.browserEvent.touches[0].clientY;
20894     },
20895     
20896     onTouchMove : function(e)
20897     {
20898         this.swiping = true;
20899         
20900         this.endX = e.browserEvent.touches[0].clientX;
20901         this.endY = e.browserEvent.touches[0].clientY;
20902     },
20903     
20904     onTouchEnd : function(e)
20905     {
20906         if(!this.swiping){
20907             this.onClick(e);
20908             return;
20909         }
20910         
20911         var tabGroup = this.parent();
20912         
20913         if(this.endX > this.startX){ // swiping right
20914             tabGroup.showPanelPrev();
20915             return;
20916         }
20917         
20918         if(this.startX > this.endX){ // swiping left
20919             tabGroup.showPanelNext();
20920             return;
20921         }
20922     }
20923     
20924     
20925 });
20926  
20927
20928  
20929
20930  /*
20931  * - LGPL
20932  *
20933  * DateField
20934  * 
20935  */
20936
20937 /**
20938  * @class Roo.bootstrap.DateField
20939  * @extends Roo.bootstrap.Input
20940  * Bootstrap DateField class
20941  * @cfg {Number} weekStart default 0
20942  * @cfg {String} viewMode default empty, (months|years)
20943  * @cfg {String} minViewMode default empty, (months|years)
20944  * @cfg {Number} startDate default -Infinity
20945  * @cfg {Number} endDate default Infinity
20946  * @cfg {Boolean} todayHighlight default false
20947  * @cfg {Boolean} todayBtn default false
20948  * @cfg {Boolean} calendarWeeks default false
20949  * @cfg {Object} daysOfWeekDisabled default empty
20950  * @cfg {Boolean} singleMode default false (true | false)
20951  * 
20952  * @cfg {Boolean} keyboardNavigation default true
20953  * @cfg {String} language default en
20954  * 
20955  * @constructor
20956  * Create a new DateField
20957  * @param {Object} config The config object
20958  */
20959
20960 Roo.bootstrap.DateField = function(config){
20961     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20962      this.addEvents({
20963             /**
20964              * @event show
20965              * Fires when this field show.
20966              * @param {Roo.bootstrap.DateField} this
20967              * @param {Mixed} date The date value
20968              */
20969             show : true,
20970             /**
20971              * @event show
20972              * Fires when this field hide.
20973              * @param {Roo.bootstrap.DateField} this
20974              * @param {Mixed} date The date value
20975              */
20976             hide : true,
20977             /**
20978              * @event select
20979              * Fires when select a date.
20980              * @param {Roo.bootstrap.DateField} this
20981              * @param {Mixed} date The date value
20982              */
20983             select : true,
20984             /**
20985              * @event beforeselect
20986              * Fires when before select a date.
20987              * @param {Roo.bootstrap.DateField} this
20988              * @param {Mixed} date The date value
20989              */
20990             beforeselect : true
20991         });
20992 };
20993
20994 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20995     
20996     /**
20997      * @cfg {String} format
20998      * The default date format string which can be overriden for localization support.  The format must be
20999      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21000      */
21001     format : "m/d/y",
21002     /**
21003      * @cfg {String} altFormats
21004      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21005      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21006      */
21007     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21008     
21009     weekStart : 0,
21010     
21011     viewMode : '',
21012     
21013     minViewMode : '',
21014     
21015     todayHighlight : false,
21016     
21017     todayBtn: false,
21018     
21019     language: 'en',
21020     
21021     keyboardNavigation: true,
21022     
21023     calendarWeeks: false,
21024     
21025     startDate: -Infinity,
21026     
21027     endDate: Infinity,
21028     
21029     daysOfWeekDisabled: [],
21030     
21031     _events: [],
21032     
21033     singleMode : false,
21034     
21035     UTCDate: function()
21036     {
21037         return new Date(Date.UTC.apply(Date, arguments));
21038     },
21039     
21040     UTCToday: function()
21041     {
21042         var today = new Date();
21043         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21044     },
21045     
21046     getDate: function() {
21047             var d = this.getUTCDate();
21048             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21049     },
21050     
21051     getUTCDate: function() {
21052             return this.date;
21053     },
21054     
21055     setDate: function(d) {
21056             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21057     },
21058     
21059     setUTCDate: function(d) {
21060             this.date = d;
21061             this.setValue(this.formatDate(this.date));
21062     },
21063         
21064     onRender: function(ct, position)
21065     {
21066         
21067         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21068         
21069         this.language = this.language || 'en';
21070         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21071         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21072         
21073         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21074         this.format = this.format || 'm/d/y';
21075         this.isInline = false;
21076         this.isInput = true;
21077         this.component = this.el.select('.add-on', true).first() || false;
21078         this.component = (this.component && this.component.length === 0) ? false : this.component;
21079         this.hasInput = this.component && this.inputEl().length;
21080         
21081         if (typeof(this.minViewMode === 'string')) {
21082             switch (this.minViewMode) {
21083                 case 'months':
21084                     this.minViewMode = 1;
21085                     break;
21086                 case 'years':
21087                     this.minViewMode = 2;
21088                     break;
21089                 default:
21090                     this.minViewMode = 0;
21091                     break;
21092             }
21093         }
21094         
21095         if (typeof(this.viewMode === 'string')) {
21096             switch (this.viewMode) {
21097                 case 'months':
21098                     this.viewMode = 1;
21099                     break;
21100                 case 'years':
21101                     this.viewMode = 2;
21102                     break;
21103                 default:
21104                     this.viewMode = 0;
21105                     break;
21106             }
21107         }
21108                 
21109         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21110         
21111 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21112         
21113         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21114         
21115         this.picker().on('mousedown', this.onMousedown, this);
21116         this.picker().on('click', this.onClick, this);
21117         
21118         this.picker().addClass('datepicker-dropdown');
21119         
21120         this.startViewMode = this.viewMode;
21121         
21122         if(this.singleMode){
21123             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21124                 v.setVisibilityMode(Roo.Element.DISPLAY);
21125                 v.hide();
21126             });
21127             
21128             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21129                 v.setStyle('width', '189px');
21130             });
21131         }
21132         
21133         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21134             if(!this.calendarWeeks){
21135                 v.remove();
21136                 return;
21137             }
21138             
21139             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21140             v.attr('colspan', function(i, val){
21141                 return parseInt(val) + 1;
21142             });
21143         });
21144                         
21145         
21146         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21147         
21148         this.setStartDate(this.startDate);
21149         this.setEndDate(this.endDate);
21150         
21151         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21152         
21153         this.fillDow();
21154         this.fillMonths();
21155         this.update();
21156         this.showMode();
21157         
21158         if(this.isInline) {
21159             this.showPopup();
21160         }
21161     },
21162     
21163     picker : function()
21164     {
21165         return this.pickerEl;
21166 //        return this.el.select('.datepicker', true).first();
21167     },
21168     
21169     fillDow: function()
21170     {
21171         var dowCnt = this.weekStart;
21172         
21173         var dow = {
21174             tag: 'tr',
21175             cn: [
21176                 
21177             ]
21178         };
21179         
21180         if(this.calendarWeeks){
21181             dow.cn.push({
21182                 tag: 'th',
21183                 cls: 'cw',
21184                 html: '&nbsp;'
21185             })
21186         }
21187         
21188         while (dowCnt < this.weekStart + 7) {
21189             dow.cn.push({
21190                 tag: 'th',
21191                 cls: 'dow',
21192                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21193             });
21194         }
21195         
21196         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21197     },
21198     
21199     fillMonths: function()
21200     {    
21201         var i = 0;
21202         var months = this.picker().select('>.datepicker-months td', true).first();
21203         
21204         months.dom.innerHTML = '';
21205         
21206         while (i < 12) {
21207             var month = {
21208                 tag: 'span',
21209                 cls: 'month',
21210                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21211             };
21212             
21213             months.createChild(month);
21214         }
21215         
21216     },
21217     
21218     update: function()
21219     {
21220         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;
21221         
21222         if (this.date < this.startDate) {
21223             this.viewDate = new Date(this.startDate);
21224         } else if (this.date > this.endDate) {
21225             this.viewDate = new Date(this.endDate);
21226         } else {
21227             this.viewDate = new Date(this.date);
21228         }
21229         
21230         this.fill();
21231     },
21232     
21233     fill: function() 
21234     {
21235         var d = new Date(this.viewDate),
21236                 year = d.getUTCFullYear(),
21237                 month = d.getUTCMonth(),
21238                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21239                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21240                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21241                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21242                 currentDate = this.date && this.date.valueOf(),
21243                 today = this.UTCToday();
21244         
21245         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21246         
21247 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21248         
21249 //        this.picker.select('>tfoot th.today').
21250 //                                              .text(dates[this.language].today)
21251 //                                              .toggle(this.todayBtn !== false);
21252     
21253         this.updateNavArrows();
21254         this.fillMonths();
21255                                                 
21256         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21257         
21258         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21259          
21260         prevMonth.setUTCDate(day);
21261         
21262         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21263         
21264         var nextMonth = new Date(prevMonth);
21265         
21266         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21267         
21268         nextMonth = nextMonth.valueOf();
21269         
21270         var fillMonths = false;
21271         
21272         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21273         
21274         while(prevMonth.valueOf() <= nextMonth) {
21275             var clsName = '';
21276             
21277             if (prevMonth.getUTCDay() === this.weekStart) {
21278                 if(fillMonths){
21279                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21280                 }
21281                     
21282                 fillMonths = {
21283                     tag: 'tr',
21284                     cn: []
21285                 };
21286                 
21287                 if(this.calendarWeeks){
21288                     // ISO 8601: First week contains first thursday.
21289                     // ISO also states week starts on Monday, but we can be more abstract here.
21290                     var
21291                     // Start of current week: based on weekstart/current date
21292                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21293                     // Thursday of this week
21294                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21295                     // First Thursday of year, year from thursday
21296                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21297                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21298                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21299                     
21300                     fillMonths.cn.push({
21301                         tag: 'td',
21302                         cls: 'cw',
21303                         html: calWeek
21304                     });
21305                 }
21306             }
21307             
21308             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21309                 clsName += ' old';
21310             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21311                 clsName += ' new';
21312             }
21313             if (this.todayHighlight &&
21314                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21315                 prevMonth.getUTCMonth() == today.getMonth() &&
21316                 prevMonth.getUTCDate() == today.getDate()) {
21317                 clsName += ' today';
21318             }
21319             
21320             if (currentDate && prevMonth.valueOf() === currentDate) {
21321                 clsName += ' active';
21322             }
21323             
21324             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21325                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21326                     clsName += ' disabled';
21327             }
21328             
21329             fillMonths.cn.push({
21330                 tag: 'td',
21331                 cls: 'day ' + clsName,
21332                 html: prevMonth.getDate()
21333             });
21334             
21335             prevMonth.setDate(prevMonth.getDate()+1);
21336         }
21337           
21338         var currentYear = this.date && this.date.getUTCFullYear();
21339         var currentMonth = this.date && this.date.getUTCMonth();
21340         
21341         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21342         
21343         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21344             v.removeClass('active');
21345             
21346             if(currentYear === year && k === currentMonth){
21347                 v.addClass('active');
21348             }
21349             
21350             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21351                 v.addClass('disabled');
21352             }
21353             
21354         });
21355         
21356         
21357         year = parseInt(year/10, 10) * 10;
21358         
21359         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21360         
21361         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21362         
21363         year -= 1;
21364         for (var i = -1; i < 11; i++) {
21365             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21366                 tag: 'span',
21367                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21368                 html: year
21369             });
21370             
21371             year += 1;
21372         }
21373     },
21374     
21375     showMode: function(dir) 
21376     {
21377         if (dir) {
21378             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21379         }
21380         
21381         Roo.each(this.picker().select('>div',true).elements, function(v){
21382             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21383             v.hide();
21384         });
21385         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21386     },
21387     
21388     place: function()
21389     {
21390         if(this.isInline) {
21391             return;
21392         }
21393         
21394         this.picker().removeClass(['bottom', 'top']);
21395         
21396         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21397             /*
21398              * place to the top of element!
21399              *
21400              */
21401             
21402             this.picker().addClass('top');
21403             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21404             
21405             return;
21406         }
21407         
21408         this.picker().addClass('bottom');
21409         
21410         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21411     },
21412     
21413     parseDate : function(value)
21414     {
21415         if(!value || value instanceof Date){
21416             return value;
21417         }
21418         var v = Date.parseDate(value, this.format);
21419         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21420             v = Date.parseDate(value, 'Y-m-d');
21421         }
21422         if(!v && this.altFormats){
21423             if(!this.altFormatsArray){
21424                 this.altFormatsArray = this.altFormats.split("|");
21425             }
21426             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21427                 v = Date.parseDate(value, this.altFormatsArray[i]);
21428             }
21429         }
21430         return v;
21431     },
21432     
21433     formatDate : function(date, fmt)
21434     {   
21435         return (!date || !(date instanceof Date)) ?
21436         date : date.dateFormat(fmt || this.format);
21437     },
21438     
21439     onFocus : function()
21440     {
21441         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21442         this.showPopup();
21443     },
21444     
21445     onBlur : function()
21446     {
21447         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21448         
21449         var d = this.inputEl().getValue();
21450         
21451         this.setValue(d);
21452                 
21453         this.hidePopup();
21454     },
21455     
21456     showPopup : function()
21457     {
21458         this.picker().show();
21459         this.update();
21460         this.place();
21461         
21462         this.fireEvent('showpopup', this, this.date);
21463     },
21464     
21465     hidePopup : function()
21466     {
21467         if(this.isInline) {
21468             return;
21469         }
21470         this.picker().hide();
21471         this.viewMode = this.startViewMode;
21472         this.showMode();
21473         
21474         this.fireEvent('hidepopup', this, this.date);
21475         
21476     },
21477     
21478     onMousedown: function(e)
21479     {
21480         e.stopPropagation();
21481         e.preventDefault();
21482     },
21483     
21484     keyup: function(e)
21485     {
21486         Roo.bootstrap.DateField.superclass.keyup.call(this);
21487         this.update();
21488     },
21489
21490     setValue: function(v)
21491     {
21492         if(this.fireEvent('beforeselect', this, v) !== false){
21493             var d = new Date(this.parseDate(v) ).clearTime();
21494         
21495             if(isNaN(d.getTime())){
21496                 this.date = this.viewDate = '';
21497                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21498                 return;
21499             }
21500
21501             v = this.formatDate(d);
21502
21503             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21504
21505             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21506
21507             this.update();
21508
21509             this.fireEvent('select', this, this.date);
21510         }
21511     },
21512     
21513     getValue: function()
21514     {
21515         return this.formatDate(this.date);
21516     },
21517     
21518     fireKey: function(e)
21519     {
21520         if (!this.picker().isVisible()){
21521             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21522                 this.showPopup();
21523             }
21524             return;
21525         }
21526         
21527         var dateChanged = false,
21528         dir, day, month,
21529         newDate, newViewDate;
21530         
21531         switch(e.keyCode){
21532             case 27: // escape
21533                 this.hidePopup();
21534                 e.preventDefault();
21535                 break;
21536             case 37: // left
21537             case 39: // right
21538                 if (!this.keyboardNavigation) {
21539                     break;
21540                 }
21541                 dir = e.keyCode == 37 ? -1 : 1;
21542                 
21543                 if (e.ctrlKey){
21544                     newDate = this.moveYear(this.date, dir);
21545                     newViewDate = this.moveYear(this.viewDate, dir);
21546                 } else if (e.shiftKey){
21547                     newDate = this.moveMonth(this.date, dir);
21548                     newViewDate = this.moveMonth(this.viewDate, dir);
21549                 } else {
21550                     newDate = new Date(this.date);
21551                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21552                     newViewDate = new Date(this.viewDate);
21553                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21554                 }
21555                 if (this.dateWithinRange(newDate)){
21556                     this.date = newDate;
21557                     this.viewDate = newViewDate;
21558                     this.setValue(this.formatDate(this.date));
21559 //                    this.update();
21560                     e.preventDefault();
21561                     dateChanged = true;
21562                 }
21563                 break;
21564             case 38: // up
21565             case 40: // down
21566                 if (!this.keyboardNavigation) {
21567                     break;
21568                 }
21569                 dir = e.keyCode == 38 ? -1 : 1;
21570                 if (e.ctrlKey){
21571                     newDate = this.moveYear(this.date, dir);
21572                     newViewDate = this.moveYear(this.viewDate, dir);
21573                 } else if (e.shiftKey){
21574                     newDate = this.moveMonth(this.date, dir);
21575                     newViewDate = this.moveMonth(this.viewDate, dir);
21576                 } else {
21577                     newDate = new Date(this.date);
21578                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21579                     newViewDate = new Date(this.viewDate);
21580                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21581                 }
21582                 if (this.dateWithinRange(newDate)){
21583                     this.date = newDate;
21584                     this.viewDate = newViewDate;
21585                     this.setValue(this.formatDate(this.date));
21586 //                    this.update();
21587                     e.preventDefault();
21588                     dateChanged = true;
21589                 }
21590                 break;
21591             case 13: // enter
21592                 this.setValue(this.formatDate(this.date));
21593                 this.hidePopup();
21594                 e.preventDefault();
21595                 break;
21596             case 9: // tab
21597                 this.setValue(this.formatDate(this.date));
21598                 this.hidePopup();
21599                 break;
21600             case 16: // shift
21601             case 17: // ctrl
21602             case 18: // alt
21603                 break;
21604             default :
21605                 this.hidePopup();
21606                 
21607         }
21608     },
21609     
21610     
21611     onClick: function(e) 
21612     {
21613         e.stopPropagation();
21614         e.preventDefault();
21615         
21616         var target = e.getTarget();
21617         
21618         if(target.nodeName.toLowerCase() === 'i'){
21619             target = Roo.get(target).dom.parentNode;
21620         }
21621         
21622         var nodeName = target.nodeName;
21623         var className = target.className;
21624         var html = target.innerHTML;
21625         //Roo.log(nodeName);
21626         
21627         switch(nodeName.toLowerCase()) {
21628             case 'th':
21629                 switch(className) {
21630                     case 'switch':
21631                         this.showMode(1);
21632                         break;
21633                     case 'prev':
21634                     case 'next':
21635                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21636                         switch(this.viewMode){
21637                                 case 0:
21638                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21639                                         break;
21640                                 case 1:
21641                                 case 2:
21642                                         this.viewDate = this.moveYear(this.viewDate, dir);
21643                                         break;
21644                         }
21645                         this.fill();
21646                         break;
21647                     case 'today':
21648                         var date = new Date();
21649                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21650 //                        this.fill()
21651                         this.setValue(this.formatDate(this.date));
21652                         
21653                         this.hidePopup();
21654                         break;
21655                 }
21656                 break;
21657             case 'span':
21658                 if (className.indexOf('disabled') < 0) {
21659                     this.viewDate.setUTCDate(1);
21660                     if (className.indexOf('month') > -1) {
21661                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21662                     } else {
21663                         var year = parseInt(html, 10) || 0;
21664                         this.viewDate.setUTCFullYear(year);
21665                         
21666                     }
21667                     
21668                     if(this.singleMode){
21669                         this.setValue(this.formatDate(this.viewDate));
21670                         this.hidePopup();
21671                         return;
21672                     }
21673                     
21674                     this.showMode(-1);
21675                     this.fill();
21676                 }
21677                 break;
21678                 
21679             case 'td':
21680                 //Roo.log(className);
21681                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21682                     var day = parseInt(html, 10) || 1;
21683                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21684                         month = (this.viewDate || new Date()).getUTCMonth();
21685
21686                     if (className.indexOf('old') > -1) {
21687                         if(month === 0 ){
21688                             month = 11;
21689                             year -= 1;
21690                         }else{
21691                             month -= 1;
21692                         }
21693                     } else if (className.indexOf('new') > -1) {
21694                         if (month == 11) {
21695                             month = 0;
21696                             year += 1;
21697                         } else {
21698                             month += 1;
21699                         }
21700                     }
21701                     //Roo.log([year,month,day]);
21702                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21703                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21704 //                    this.fill();
21705                     //Roo.log(this.formatDate(this.date));
21706                     this.setValue(this.formatDate(this.date));
21707                     this.hidePopup();
21708                 }
21709                 break;
21710         }
21711     },
21712     
21713     setStartDate: function(startDate)
21714     {
21715         this.startDate = startDate || -Infinity;
21716         if (this.startDate !== -Infinity) {
21717             this.startDate = this.parseDate(this.startDate);
21718         }
21719         this.update();
21720         this.updateNavArrows();
21721     },
21722
21723     setEndDate: function(endDate)
21724     {
21725         this.endDate = endDate || Infinity;
21726         if (this.endDate !== Infinity) {
21727             this.endDate = this.parseDate(this.endDate);
21728         }
21729         this.update();
21730         this.updateNavArrows();
21731     },
21732     
21733     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21734     {
21735         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21736         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21737             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21738         }
21739         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21740             return parseInt(d, 10);
21741         });
21742         this.update();
21743         this.updateNavArrows();
21744     },
21745     
21746     updateNavArrows: function() 
21747     {
21748         if(this.singleMode){
21749             return;
21750         }
21751         
21752         var d = new Date(this.viewDate),
21753         year = d.getUTCFullYear(),
21754         month = d.getUTCMonth();
21755         
21756         Roo.each(this.picker().select('.prev', true).elements, function(v){
21757             v.show();
21758             switch (this.viewMode) {
21759                 case 0:
21760
21761                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21762                         v.hide();
21763                     }
21764                     break;
21765                 case 1:
21766                 case 2:
21767                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21768                         v.hide();
21769                     }
21770                     break;
21771             }
21772         });
21773         
21774         Roo.each(this.picker().select('.next', true).elements, function(v){
21775             v.show();
21776             switch (this.viewMode) {
21777                 case 0:
21778
21779                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21780                         v.hide();
21781                     }
21782                     break;
21783                 case 1:
21784                 case 2:
21785                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21786                         v.hide();
21787                     }
21788                     break;
21789             }
21790         })
21791     },
21792     
21793     moveMonth: function(date, dir)
21794     {
21795         if (!dir) {
21796             return date;
21797         }
21798         var new_date = new Date(date.valueOf()),
21799         day = new_date.getUTCDate(),
21800         month = new_date.getUTCMonth(),
21801         mag = Math.abs(dir),
21802         new_month, test;
21803         dir = dir > 0 ? 1 : -1;
21804         if (mag == 1){
21805             test = dir == -1
21806             // If going back one month, make sure month is not current month
21807             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21808             ? function(){
21809                 return new_date.getUTCMonth() == month;
21810             }
21811             // If going forward one month, make sure month is as expected
21812             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21813             : function(){
21814                 return new_date.getUTCMonth() != new_month;
21815             };
21816             new_month = month + dir;
21817             new_date.setUTCMonth(new_month);
21818             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21819             if (new_month < 0 || new_month > 11) {
21820                 new_month = (new_month + 12) % 12;
21821             }
21822         } else {
21823             // For magnitudes >1, move one month at a time...
21824             for (var i=0; i<mag; i++) {
21825                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21826                 new_date = this.moveMonth(new_date, dir);
21827             }
21828             // ...then reset the day, keeping it in the new month
21829             new_month = new_date.getUTCMonth();
21830             new_date.setUTCDate(day);
21831             test = function(){
21832                 return new_month != new_date.getUTCMonth();
21833             };
21834         }
21835         // Common date-resetting loop -- if date is beyond end of month, make it
21836         // end of month
21837         while (test()){
21838             new_date.setUTCDate(--day);
21839             new_date.setUTCMonth(new_month);
21840         }
21841         return new_date;
21842     },
21843
21844     moveYear: function(date, dir)
21845     {
21846         return this.moveMonth(date, dir*12);
21847     },
21848
21849     dateWithinRange: function(date)
21850     {
21851         return date >= this.startDate && date <= this.endDate;
21852     },
21853
21854     
21855     remove: function() 
21856     {
21857         this.picker().remove();
21858     },
21859     
21860     validateValue : function(value)
21861     {
21862         if(this.getVisibilityEl().hasClass('hidden')){
21863             return true;
21864         }
21865         
21866         if(value.length < 1)  {
21867             if(this.allowBlank){
21868                 return true;
21869             }
21870             return false;
21871         }
21872         
21873         if(value.length < this.minLength){
21874             return false;
21875         }
21876         if(value.length > this.maxLength){
21877             return false;
21878         }
21879         if(this.vtype){
21880             var vt = Roo.form.VTypes;
21881             if(!vt[this.vtype](value, this)){
21882                 return false;
21883             }
21884         }
21885         if(typeof this.validator == "function"){
21886             var msg = this.validator(value);
21887             if(msg !== true){
21888                 return false;
21889             }
21890         }
21891         
21892         if(this.regex && !this.regex.test(value)){
21893             return false;
21894         }
21895         
21896         if(typeof(this.parseDate(value)) == 'undefined'){
21897             return false;
21898         }
21899         
21900         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21901             return false;
21902         }      
21903         
21904         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21905             return false;
21906         } 
21907         
21908         
21909         return true;
21910     },
21911     
21912     reset : function()
21913     {
21914         this.date = this.viewDate = '';
21915         
21916         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21917     }
21918    
21919 });
21920
21921 Roo.apply(Roo.bootstrap.DateField,  {
21922     
21923     head : {
21924         tag: 'thead',
21925         cn: [
21926         {
21927             tag: 'tr',
21928             cn: [
21929             {
21930                 tag: 'th',
21931                 cls: 'prev',
21932                 html: '<i class="fa fa-arrow-left"/>'
21933             },
21934             {
21935                 tag: 'th',
21936                 cls: 'switch',
21937                 colspan: '5'
21938             },
21939             {
21940                 tag: 'th',
21941                 cls: 'next',
21942                 html: '<i class="fa fa-arrow-right"/>'
21943             }
21944
21945             ]
21946         }
21947         ]
21948     },
21949     
21950     content : {
21951         tag: 'tbody',
21952         cn: [
21953         {
21954             tag: 'tr',
21955             cn: [
21956             {
21957                 tag: 'td',
21958                 colspan: '7'
21959             }
21960             ]
21961         }
21962         ]
21963     },
21964     
21965     footer : {
21966         tag: 'tfoot',
21967         cn: [
21968         {
21969             tag: 'tr',
21970             cn: [
21971             {
21972                 tag: 'th',
21973                 colspan: '7',
21974                 cls: 'today'
21975             }
21976                     
21977             ]
21978         }
21979         ]
21980     },
21981     
21982     dates:{
21983         en: {
21984             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21985             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21986             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21987             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21988             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21989             today: "Today"
21990         }
21991     },
21992     
21993     modes: [
21994     {
21995         clsName: 'days',
21996         navFnc: 'Month',
21997         navStep: 1
21998     },
21999     {
22000         clsName: 'months',
22001         navFnc: 'FullYear',
22002         navStep: 1
22003     },
22004     {
22005         clsName: 'years',
22006         navFnc: 'FullYear',
22007         navStep: 10
22008     }]
22009 });
22010
22011 Roo.apply(Roo.bootstrap.DateField,  {
22012   
22013     template : {
22014         tag: 'div',
22015         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22016         cn: [
22017         {
22018             tag: 'div',
22019             cls: 'datepicker-days',
22020             cn: [
22021             {
22022                 tag: 'table',
22023                 cls: 'table-condensed',
22024                 cn:[
22025                 Roo.bootstrap.DateField.head,
22026                 {
22027                     tag: 'tbody'
22028                 },
22029                 Roo.bootstrap.DateField.footer
22030                 ]
22031             }
22032             ]
22033         },
22034         {
22035             tag: 'div',
22036             cls: 'datepicker-months',
22037             cn: [
22038             {
22039                 tag: 'table',
22040                 cls: 'table-condensed',
22041                 cn:[
22042                 Roo.bootstrap.DateField.head,
22043                 Roo.bootstrap.DateField.content,
22044                 Roo.bootstrap.DateField.footer
22045                 ]
22046             }
22047             ]
22048         },
22049         {
22050             tag: 'div',
22051             cls: 'datepicker-years',
22052             cn: [
22053             {
22054                 tag: 'table',
22055                 cls: 'table-condensed',
22056                 cn:[
22057                 Roo.bootstrap.DateField.head,
22058                 Roo.bootstrap.DateField.content,
22059                 Roo.bootstrap.DateField.footer
22060                 ]
22061             }
22062             ]
22063         }
22064         ]
22065     }
22066 });
22067
22068  
22069
22070  /*
22071  * - LGPL
22072  *
22073  * TimeField
22074  * 
22075  */
22076
22077 /**
22078  * @class Roo.bootstrap.TimeField
22079  * @extends Roo.bootstrap.Input
22080  * Bootstrap DateField class
22081  * 
22082  * 
22083  * @constructor
22084  * Create a new TimeField
22085  * @param {Object} config The config object
22086  */
22087
22088 Roo.bootstrap.TimeField = function(config){
22089     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22090     this.addEvents({
22091             /**
22092              * @event show
22093              * Fires when this field show.
22094              * @param {Roo.bootstrap.DateField} thisthis
22095              * @param {Mixed} date The date value
22096              */
22097             show : true,
22098             /**
22099              * @event show
22100              * Fires when this field hide.
22101              * @param {Roo.bootstrap.DateField} this
22102              * @param {Mixed} date The date value
22103              */
22104             hide : true,
22105             /**
22106              * @event select
22107              * Fires when select a date.
22108              * @param {Roo.bootstrap.DateField} this
22109              * @param {Mixed} date The date value
22110              */
22111             select : true
22112         });
22113 };
22114
22115 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22116     
22117     /**
22118      * @cfg {String} format
22119      * The default time format string which can be overriden for localization support.  The format must be
22120      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22121      */
22122     format : "H:i",
22123
22124     getAutoCreate : function()
22125     {
22126         this.after = '<i class="fa far fa-clock"></i>';
22127         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22128         
22129          
22130     },
22131     onRender: function(ct, position)
22132     {
22133         
22134         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22135                 
22136         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22137         
22138         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22139         
22140         this.pop = this.picker().select('>.datepicker-time',true).first();
22141         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22142         
22143         this.picker().on('mousedown', this.onMousedown, this);
22144         this.picker().on('click', this.onClick, this);
22145         
22146         this.picker().addClass('datepicker-dropdown');
22147     
22148         this.fillTime();
22149         this.update();
22150             
22151         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22152         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22153         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22154         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22155         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22156         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22157
22158     },
22159     
22160     fireKey: function(e){
22161         if (!this.picker().isVisible()){
22162             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22163                 this.show();
22164             }
22165             return;
22166         }
22167
22168         e.preventDefault();
22169         
22170         switch(e.keyCode){
22171             case 27: // escape
22172                 this.hide();
22173                 break;
22174             case 37: // left
22175             case 39: // right
22176                 this.onTogglePeriod();
22177                 break;
22178             case 38: // up
22179                 this.onIncrementMinutes();
22180                 break;
22181             case 40: // down
22182                 this.onDecrementMinutes();
22183                 break;
22184             case 13: // enter
22185             case 9: // tab
22186                 this.setTime();
22187                 break;
22188         }
22189     },
22190     
22191     onClick: function(e) {
22192         e.stopPropagation();
22193         e.preventDefault();
22194     },
22195     
22196     picker : function()
22197     {
22198         return this.pickerEl;
22199     },
22200     
22201     fillTime: function()
22202     {    
22203         var time = this.pop.select('tbody', true).first();
22204         
22205         time.dom.innerHTML = '';
22206         
22207         time.createChild({
22208             tag: 'tr',
22209             cn: [
22210                 {
22211                     tag: 'td',
22212                     cn: [
22213                         {
22214                             tag: 'a',
22215                             href: '#',
22216                             cls: 'btn',
22217                             cn: [
22218                                 {
22219                                     tag: 'i',
22220                                     cls: 'hours-up fa fas fa-chevron-up'
22221                                 }
22222                             ]
22223                         } 
22224                     ]
22225                 },
22226                 {
22227                     tag: 'td',
22228                     cls: 'separator'
22229                 },
22230                 {
22231                     tag: 'td',
22232                     cn: [
22233                         {
22234                             tag: 'a',
22235                             href: '#',
22236                             cls: 'btn',
22237                             cn: [
22238                                 {
22239                                     tag: 'i',
22240                                     cls: 'minutes-up fa fas fa-chevron-up'
22241                                 }
22242                             ]
22243                         }
22244                     ]
22245                 },
22246                 {
22247                     tag: 'td',
22248                     cls: 'separator'
22249                 }
22250             ]
22251         });
22252         
22253         time.createChild({
22254             tag: 'tr',
22255             cn: [
22256                 {
22257                     tag: 'td',
22258                     cn: [
22259                         {
22260                             tag: 'span',
22261                             cls: 'timepicker-hour',
22262                             html: '00'
22263                         }  
22264                     ]
22265                 },
22266                 {
22267                     tag: 'td',
22268                     cls: 'separator',
22269                     html: ':'
22270                 },
22271                 {
22272                     tag: 'td',
22273                     cn: [
22274                         {
22275                             tag: 'span',
22276                             cls: 'timepicker-minute',
22277                             html: '00'
22278                         }  
22279                     ]
22280                 },
22281                 {
22282                     tag: 'td',
22283                     cls: 'separator'
22284                 },
22285                 {
22286                     tag: 'td',
22287                     cn: [
22288                         {
22289                             tag: 'button',
22290                             type: 'button',
22291                             cls: 'btn btn-primary period',
22292                             html: 'AM'
22293                             
22294                         }
22295                     ]
22296                 }
22297             ]
22298         });
22299         
22300         time.createChild({
22301             tag: 'tr',
22302             cn: [
22303                 {
22304                     tag: 'td',
22305                     cn: [
22306                         {
22307                             tag: 'a',
22308                             href: '#',
22309                             cls: 'btn',
22310                             cn: [
22311                                 {
22312                                     tag: 'span',
22313                                     cls: 'hours-down fa fas fa-chevron-down'
22314                                 }
22315                             ]
22316                         }
22317                     ]
22318                 },
22319                 {
22320                     tag: 'td',
22321                     cls: 'separator'
22322                 },
22323                 {
22324                     tag: 'td',
22325                     cn: [
22326                         {
22327                             tag: 'a',
22328                             href: '#',
22329                             cls: 'btn',
22330                             cn: [
22331                                 {
22332                                     tag: 'span',
22333                                     cls: 'minutes-down fa fas fa-chevron-down'
22334                                 }
22335                             ]
22336                         }
22337                     ]
22338                 },
22339                 {
22340                     tag: 'td',
22341                     cls: 'separator'
22342                 }
22343             ]
22344         });
22345         
22346     },
22347     
22348     update: function()
22349     {
22350         
22351         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22352         
22353         this.fill();
22354     },
22355     
22356     fill: function() 
22357     {
22358         var hours = this.time.getHours();
22359         var minutes = this.time.getMinutes();
22360         var period = 'AM';
22361         
22362         if(hours > 11){
22363             period = 'PM';
22364         }
22365         
22366         if(hours == 0){
22367             hours = 12;
22368         }
22369         
22370         
22371         if(hours > 12){
22372             hours = hours - 12;
22373         }
22374         
22375         if(hours < 10){
22376             hours = '0' + hours;
22377         }
22378         
22379         if(minutes < 10){
22380             minutes = '0' + minutes;
22381         }
22382         
22383         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22384         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22385         this.pop.select('button', true).first().dom.innerHTML = period;
22386         
22387     },
22388     
22389     place: function()
22390     {   
22391         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22392         
22393         var cls = ['bottom'];
22394         
22395         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22396             cls.pop();
22397             cls.push('top');
22398         }
22399         
22400         cls.push('right');
22401         
22402         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22403             cls.pop();
22404             cls.push('left');
22405         }
22406         //this.picker().setXY(20000,20000);
22407         this.picker().addClass(cls.join('-'));
22408         
22409         var _this = this;
22410         
22411         Roo.each(cls, function(c){
22412             if(c == 'bottom'){
22413                 (function() {
22414                  //  
22415                 }).defer(200);
22416                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22417                 //_this.picker().setTop(_this.inputEl().getHeight());
22418                 return;
22419             }
22420             if(c == 'top'){
22421                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22422                 
22423                 //_this.picker().setTop(0 - _this.picker().getHeight());
22424                 return;
22425             }
22426             /*
22427             if(c == 'left'){
22428                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22429                 return;
22430             }
22431             if(c == 'right'){
22432                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22433                 return;
22434             }
22435             */
22436         });
22437         
22438     },
22439   
22440     onFocus : function()
22441     {
22442         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22443         this.show();
22444     },
22445     
22446     onBlur : function()
22447     {
22448         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22449         this.hide();
22450     },
22451     
22452     show : function()
22453     {
22454         this.picker().show();
22455         this.pop.show();
22456         this.update();
22457         this.place();
22458         
22459         this.fireEvent('show', this, this.date);
22460     },
22461     
22462     hide : function()
22463     {
22464         this.picker().hide();
22465         this.pop.hide();
22466         
22467         this.fireEvent('hide', this, this.date);
22468     },
22469     
22470     setTime : function()
22471     {
22472         this.hide();
22473         this.setValue(this.time.format(this.format));
22474         
22475         this.fireEvent('select', this, this.date);
22476         
22477         
22478     },
22479     
22480     onMousedown: function(e){
22481         e.stopPropagation();
22482         e.preventDefault();
22483     },
22484     
22485     onIncrementHours: function()
22486     {
22487         Roo.log('onIncrementHours');
22488         this.time = this.time.add(Date.HOUR, 1);
22489         this.update();
22490         
22491     },
22492     
22493     onDecrementHours: function()
22494     {
22495         Roo.log('onDecrementHours');
22496         this.time = this.time.add(Date.HOUR, -1);
22497         this.update();
22498     },
22499     
22500     onIncrementMinutes: function()
22501     {
22502         Roo.log('onIncrementMinutes');
22503         this.time = this.time.add(Date.MINUTE, 1);
22504         this.update();
22505     },
22506     
22507     onDecrementMinutes: function()
22508     {
22509         Roo.log('onDecrementMinutes');
22510         this.time = this.time.add(Date.MINUTE, -1);
22511         this.update();
22512     },
22513     
22514     onTogglePeriod: function()
22515     {
22516         Roo.log('onTogglePeriod');
22517         this.time = this.time.add(Date.HOUR, 12);
22518         this.update();
22519     }
22520     
22521    
22522 });
22523  
22524
22525 Roo.apply(Roo.bootstrap.TimeField,  {
22526   
22527     template : {
22528         tag: 'div',
22529         cls: 'datepicker dropdown-menu',
22530         cn: [
22531             {
22532                 tag: 'div',
22533                 cls: 'datepicker-time',
22534                 cn: [
22535                 {
22536                     tag: 'table',
22537                     cls: 'table-condensed',
22538                     cn:[
22539                         {
22540                             tag: 'tbody',
22541                             cn: [
22542                                 {
22543                                     tag: 'tr',
22544                                     cn: [
22545                                     {
22546                                         tag: 'td',
22547                                         colspan: '7'
22548                                     }
22549                                     ]
22550                                 }
22551                             ]
22552                         },
22553                         {
22554                             tag: 'tfoot',
22555                             cn: [
22556                                 {
22557                                     tag: 'tr',
22558                                     cn: [
22559                                     {
22560                                         tag: 'th',
22561                                         colspan: '7',
22562                                         cls: '',
22563                                         cn: [
22564                                             {
22565                                                 tag: 'button',
22566                                                 cls: 'btn btn-info ok',
22567                                                 html: 'OK'
22568                                             }
22569                                         ]
22570                                     }
22571                     
22572                                     ]
22573                                 }
22574                             ]
22575                         }
22576                     ]
22577                 }
22578                 ]
22579             }
22580         ]
22581     }
22582 });
22583
22584  
22585
22586  /*
22587  * - LGPL
22588  *
22589  * MonthField
22590  * 
22591  */
22592
22593 /**
22594  * @class Roo.bootstrap.MonthField
22595  * @extends Roo.bootstrap.Input
22596  * Bootstrap MonthField class
22597  * 
22598  * @cfg {String} language default en
22599  * 
22600  * @constructor
22601  * Create a new MonthField
22602  * @param {Object} config The config object
22603  */
22604
22605 Roo.bootstrap.MonthField = function(config){
22606     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22607     
22608     this.addEvents({
22609         /**
22610          * @event show
22611          * Fires when this field show.
22612          * @param {Roo.bootstrap.MonthField} this
22613          * @param {Mixed} date The date value
22614          */
22615         show : true,
22616         /**
22617          * @event show
22618          * Fires when this field hide.
22619          * @param {Roo.bootstrap.MonthField} this
22620          * @param {Mixed} date The date value
22621          */
22622         hide : true,
22623         /**
22624          * @event select
22625          * Fires when select a date.
22626          * @param {Roo.bootstrap.MonthField} this
22627          * @param {String} oldvalue The old value
22628          * @param {String} newvalue The new value
22629          */
22630         select : true
22631     });
22632 };
22633
22634 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22635     
22636     onRender: function(ct, position)
22637     {
22638         
22639         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22640         
22641         this.language = this.language || 'en';
22642         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22643         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22644         
22645         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22646         this.isInline = false;
22647         this.isInput = true;
22648         this.component = this.el.select('.add-on', true).first() || false;
22649         this.component = (this.component && this.component.length === 0) ? false : this.component;
22650         this.hasInput = this.component && this.inputEL().length;
22651         
22652         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22653         
22654         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22655         
22656         this.picker().on('mousedown', this.onMousedown, this);
22657         this.picker().on('click', this.onClick, this);
22658         
22659         this.picker().addClass('datepicker-dropdown');
22660         
22661         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22662             v.setStyle('width', '189px');
22663         });
22664         
22665         this.fillMonths();
22666         
22667         this.update();
22668         
22669         if(this.isInline) {
22670             this.show();
22671         }
22672         
22673     },
22674     
22675     setValue: function(v, suppressEvent)
22676     {   
22677         var o = this.getValue();
22678         
22679         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22680         
22681         this.update();
22682
22683         if(suppressEvent !== true){
22684             this.fireEvent('select', this, o, v);
22685         }
22686         
22687     },
22688     
22689     getValue: function()
22690     {
22691         return this.value;
22692     },
22693     
22694     onClick: function(e) 
22695     {
22696         e.stopPropagation();
22697         e.preventDefault();
22698         
22699         var target = e.getTarget();
22700         
22701         if(target.nodeName.toLowerCase() === 'i'){
22702             target = Roo.get(target).dom.parentNode;
22703         }
22704         
22705         var nodeName = target.nodeName;
22706         var className = target.className;
22707         var html = target.innerHTML;
22708         
22709         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22710             return;
22711         }
22712         
22713         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22714         
22715         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22716         
22717         this.hide();
22718                         
22719     },
22720     
22721     picker : function()
22722     {
22723         return this.pickerEl;
22724     },
22725     
22726     fillMonths: function()
22727     {    
22728         var i = 0;
22729         var months = this.picker().select('>.datepicker-months td', true).first();
22730         
22731         months.dom.innerHTML = '';
22732         
22733         while (i < 12) {
22734             var month = {
22735                 tag: 'span',
22736                 cls: 'month',
22737                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22738             };
22739             
22740             months.createChild(month);
22741         }
22742         
22743     },
22744     
22745     update: function()
22746     {
22747         var _this = this;
22748         
22749         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22750             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22751         }
22752         
22753         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22754             e.removeClass('active');
22755             
22756             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22757                 e.addClass('active');
22758             }
22759         })
22760     },
22761     
22762     place: function()
22763     {
22764         if(this.isInline) {
22765             return;
22766         }
22767         
22768         this.picker().removeClass(['bottom', 'top']);
22769         
22770         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22771             /*
22772              * place to the top of element!
22773              *
22774              */
22775             
22776             this.picker().addClass('top');
22777             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22778             
22779             return;
22780         }
22781         
22782         this.picker().addClass('bottom');
22783         
22784         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22785     },
22786     
22787     onFocus : function()
22788     {
22789         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22790         this.show();
22791     },
22792     
22793     onBlur : function()
22794     {
22795         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22796         
22797         var d = this.inputEl().getValue();
22798         
22799         this.setValue(d);
22800                 
22801         this.hide();
22802     },
22803     
22804     show : function()
22805     {
22806         this.picker().show();
22807         this.picker().select('>.datepicker-months', true).first().show();
22808         this.update();
22809         this.place();
22810         
22811         this.fireEvent('show', this, this.date);
22812     },
22813     
22814     hide : function()
22815     {
22816         if(this.isInline) {
22817             return;
22818         }
22819         this.picker().hide();
22820         this.fireEvent('hide', this, this.date);
22821         
22822     },
22823     
22824     onMousedown: function(e)
22825     {
22826         e.stopPropagation();
22827         e.preventDefault();
22828     },
22829     
22830     keyup: function(e)
22831     {
22832         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22833         this.update();
22834     },
22835
22836     fireKey: function(e)
22837     {
22838         if (!this.picker().isVisible()){
22839             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22840                 this.show();
22841             }
22842             return;
22843         }
22844         
22845         var dir;
22846         
22847         switch(e.keyCode){
22848             case 27: // escape
22849                 this.hide();
22850                 e.preventDefault();
22851                 break;
22852             case 37: // left
22853             case 39: // right
22854                 dir = e.keyCode == 37 ? -1 : 1;
22855                 
22856                 this.vIndex = this.vIndex + dir;
22857                 
22858                 if(this.vIndex < 0){
22859                     this.vIndex = 0;
22860                 }
22861                 
22862                 if(this.vIndex > 11){
22863                     this.vIndex = 11;
22864                 }
22865                 
22866                 if(isNaN(this.vIndex)){
22867                     this.vIndex = 0;
22868                 }
22869                 
22870                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22871                 
22872                 break;
22873             case 38: // up
22874             case 40: // down
22875                 
22876                 dir = e.keyCode == 38 ? -1 : 1;
22877                 
22878                 this.vIndex = this.vIndex + dir * 4;
22879                 
22880                 if(this.vIndex < 0){
22881                     this.vIndex = 0;
22882                 }
22883                 
22884                 if(this.vIndex > 11){
22885                     this.vIndex = 11;
22886                 }
22887                 
22888                 if(isNaN(this.vIndex)){
22889                     this.vIndex = 0;
22890                 }
22891                 
22892                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22893                 break;
22894                 
22895             case 13: // enter
22896                 
22897                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22898                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22899                 }
22900                 
22901                 this.hide();
22902                 e.preventDefault();
22903                 break;
22904             case 9: // tab
22905                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22906                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22907                 }
22908                 this.hide();
22909                 break;
22910             case 16: // shift
22911             case 17: // ctrl
22912             case 18: // alt
22913                 break;
22914             default :
22915                 this.hide();
22916                 
22917         }
22918     },
22919     
22920     remove: function() 
22921     {
22922         this.picker().remove();
22923     }
22924    
22925 });
22926
22927 Roo.apply(Roo.bootstrap.MonthField,  {
22928     
22929     content : {
22930         tag: 'tbody',
22931         cn: [
22932         {
22933             tag: 'tr',
22934             cn: [
22935             {
22936                 tag: 'td',
22937                 colspan: '7'
22938             }
22939             ]
22940         }
22941         ]
22942     },
22943     
22944     dates:{
22945         en: {
22946             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22947             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22948         }
22949     }
22950 });
22951
22952 Roo.apply(Roo.bootstrap.MonthField,  {
22953   
22954     template : {
22955         tag: 'div',
22956         cls: 'datepicker dropdown-menu roo-dynamic',
22957         cn: [
22958             {
22959                 tag: 'div',
22960                 cls: 'datepicker-months',
22961                 cn: [
22962                 {
22963                     tag: 'table',
22964                     cls: 'table-condensed',
22965                     cn:[
22966                         Roo.bootstrap.DateField.content
22967                     ]
22968                 }
22969                 ]
22970             }
22971         ]
22972     }
22973 });
22974
22975  
22976
22977  
22978  /*
22979  * - LGPL
22980  *
22981  * CheckBox
22982  * 
22983  */
22984
22985 /**
22986  * @class Roo.bootstrap.CheckBox
22987  * @extends Roo.bootstrap.Input
22988  * Bootstrap CheckBox class
22989  * 
22990  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22991  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22992  * @cfg {String} boxLabel The text that appears beside the checkbox
22993  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22994  * @cfg {Boolean} checked initnal the element
22995  * @cfg {Boolean} inline inline the element (default false)
22996  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22997  * @cfg {String} tooltip label tooltip
22998  * 
22999  * @constructor
23000  * Create a new CheckBox
23001  * @param {Object} config The config object
23002  */
23003
23004 Roo.bootstrap.CheckBox = function(config){
23005     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23006    
23007     this.addEvents({
23008         /**
23009         * @event check
23010         * Fires when the element is checked or unchecked.
23011         * @param {Roo.bootstrap.CheckBox} this This input
23012         * @param {Boolean} checked The new checked value
23013         */
23014        check : true,
23015        /**
23016         * @event click
23017         * Fires when the element is click.
23018         * @param {Roo.bootstrap.CheckBox} this This input
23019         */
23020        click : true
23021     });
23022     
23023 };
23024
23025 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23026   
23027     inputType: 'checkbox',
23028     inputValue: 1,
23029     valueOff: 0,
23030     boxLabel: false,
23031     checked: false,
23032     weight : false,
23033     inline: false,
23034     tooltip : '',
23035     
23036     // checkbox success does not make any sense really.. 
23037     invalidClass : "",
23038     validClass : "",
23039     
23040     
23041     getAutoCreate : function()
23042     {
23043         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23044         
23045         var id = Roo.id();
23046         
23047         var cfg = {};
23048         
23049         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23050         
23051         if(this.inline){
23052             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23053         }
23054         
23055         var input =  {
23056             tag: 'input',
23057             id : id,
23058             type : this.inputType,
23059             value : this.inputValue,
23060             cls : 'roo-' + this.inputType, //'form-box',
23061             placeholder : this.placeholder || ''
23062             
23063         };
23064         
23065         if(this.inputType != 'radio'){
23066             var hidden =  {
23067                 tag: 'input',
23068                 type : 'hidden',
23069                 cls : 'roo-hidden-value',
23070                 value : this.checked ? this.inputValue : this.valueOff
23071             };
23072         }
23073         
23074             
23075         if (this.weight) { // Validity check?
23076             cfg.cls += " " + this.inputType + "-" + this.weight;
23077         }
23078         
23079         if (this.disabled) {
23080             input.disabled=true;
23081         }
23082         
23083         if(this.checked){
23084             input.checked = this.checked;
23085         }
23086         
23087         if (this.name) {
23088             
23089             input.name = this.name;
23090             
23091             if(this.inputType != 'radio'){
23092                 hidden.name = this.name;
23093                 input.name = '_hidden_' + this.name;
23094             }
23095         }
23096         
23097         if (this.size) {
23098             input.cls += ' input-' + this.size;
23099         }
23100         
23101         var settings=this;
23102         
23103         ['xs','sm','md','lg'].map(function(size){
23104             if (settings[size]) {
23105                 cfg.cls += ' col-' + size + '-' + settings[size];
23106             }
23107         });
23108         
23109         var inputblock = input;
23110          
23111         if (this.before || this.after) {
23112             
23113             inputblock = {
23114                 cls : 'input-group',
23115                 cn :  [] 
23116             };
23117             
23118             if (this.before) {
23119                 inputblock.cn.push({
23120                     tag :'span',
23121                     cls : 'input-group-addon',
23122                     html : this.before
23123                 });
23124             }
23125             
23126             inputblock.cn.push(input);
23127             
23128             if(this.inputType != 'radio'){
23129                 inputblock.cn.push(hidden);
23130             }
23131             
23132             if (this.after) {
23133                 inputblock.cn.push({
23134                     tag :'span',
23135                     cls : 'input-group-addon',
23136                     html : this.after
23137                 });
23138             }
23139             
23140         }
23141         var boxLabelCfg = false;
23142         
23143         if(this.boxLabel){
23144            
23145             boxLabelCfg = {
23146                 tag: 'label',
23147                 //'for': id, // box label is handled by onclick - so no for...
23148                 cls: 'box-label',
23149                 html: this.boxLabel
23150             };
23151             if(this.tooltip){
23152                 boxLabelCfg.tooltip = this.tooltip;
23153             }
23154              
23155         }
23156         
23157         
23158         if (align ==='left' && this.fieldLabel.length) {
23159 //                Roo.log("left and has label");
23160             cfg.cn = [
23161                 {
23162                     tag: 'label',
23163                     'for' :  id,
23164                     cls : 'control-label',
23165                     html : this.fieldLabel
23166                 },
23167                 {
23168                     cls : "", 
23169                     cn: [
23170                         inputblock
23171                     ]
23172                 }
23173             ];
23174             
23175             if (boxLabelCfg) {
23176                 cfg.cn[1].cn.push(boxLabelCfg);
23177             }
23178             
23179             if(this.labelWidth > 12){
23180                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23181             }
23182             
23183             if(this.labelWidth < 13 && this.labelmd == 0){
23184                 this.labelmd = this.labelWidth;
23185             }
23186             
23187             if(this.labellg > 0){
23188                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23189                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23190             }
23191             
23192             if(this.labelmd > 0){
23193                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23194                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23195             }
23196             
23197             if(this.labelsm > 0){
23198                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23199                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23200             }
23201             
23202             if(this.labelxs > 0){
23203                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23204                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23205             }
23206             
23207         } else if ( this.fieldLabel.length) {
23208 //                Roo.log(" label");
23209                 cfg.cn = [
23210                    
23211                     {
23212                         tag: this.boxLabel ? 'span' : 'label',
23213                         'for': id,
23214                         cls: 'control-label box-input-label',
23215                         //cls : 'input-group-addon',
23216                         html : this.fieldLabel
23217                     },
23218                     
23219                     inputblock
23220                     
23221                 ];
23222                 if (boxLabelCfg) {
23223                     cfg.cn.push(boxLabelCfg);
23224                 }
23225
23226         } else {
23227             
23228 //                Roo.log(" no label && no align");
23229                 cfg.cn = [  inputblock ] ;
23230                 if (boxLabelCfg) {
23231                     cfg.cn.push(boxLabelCfg);
23232                 }
23233
23234                 
23235         }
23236         
23237        
23238         
23239         if(this.inputType != 'radio'){
23240             cfg.cn.push(hidden);
23241         }
23242         
23243         return cfg;
23244         
23245     },
23246     
23247     /**
23248      * return the real input element.
23249      */
23250     inputEl: function ()
23251     {
23252         return this.el.select('input.roo-' + this.inputType,true).first();
23253     },
23254     hiddenEl: function ()
23255     {
23256         return this.el.select('input.roo-hidden-value',true).first();
23257     },
23258     
23259     labelEl: function()
23260     {
23261         return this.el.select('label.control-label',true).first();
23262     },
23263     /* depricated... */
23264     
23265     label: function()
23266     {
23267         return this.labelEl();
23268     },
23269     
23270     boxLabelEl: function()
23271     {
23272         return this.el.select('label.box-label',true).first();
23273     },
23274     
23275     initEvents : function()
23276     {
23277 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23278         
23279         this.inputEl().on('click', this.onClick,  this);
23280         
23281         if (this.boxLabel) { 
23282             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23283         }
23284         
23285         this.startValue = this.getValue();
23286         
23287         if(this.groupId){
23288             Roo.bootstrap.CheckBox.register(this);
23289         }
23290     },
23291     
23292     onClick : function(e)
23293     {   
23294         if(this.fireEvent('click', this, e) !== false){
23295             this.setChecked(!this.checked);
23296         }
23297         
23298     },
23299     
23300     setChecked : function(state,suppressEvent)
23301     {
23302         this.startValue = this.getValue();
23303
23304         if(this.inputType == 'radio'){
23305             
23306             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23307                 e.dom.checked = false;
23308             });
23309             
23310             this.inputEl().dom.checked = true;
23311             
23312             this.inputEl().dom.value = this.inputValue;
23313             
23314             if(suppressEvent !== true){
23315                 this.fireEvent('check', this, true);
23316             }
23317             
23318             this.validate();
23319             
23320             return;
23321         }
23322         
23323         this.checked = state;
23324         
23325         this.inputEl().dom.checked = state;
23326         
23327         
23328         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23329         
23330         if(suppressEvent !== true){
23331             this.fireEvent('check', this, state);
23332         }
23333         
23334         this.validate();
23335     },
23336     
23337     getValue : function()
23338     {
23339         if(this.inputType == 'radio'){
23340             return this.getGroupValue();
23341         }
23342         
23343         return this.hiddenEl().dom.value;
23344         
23345     },
23346     
23347     getGroupValue : function()
23348     {
23349         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23350             return '';
23351         }
23352         
23353         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23354     },
23355     
23356     setValue : function(v,suppressEvent)
23357     {
23358         if(this.inputType == 'radio'){
23359             this.setGroupValue(v, suppressEvent);
23360             return;
23361         }
23362         
23363         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23364         
23365         this.validate();
23366     },
23367     
23368     setGroupValue : function(v, suppressEvent)
23369     {
23370         this.startValue = this.getValue();
23371         
23372         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23373             e.dom.checked = false;
23374             
23375             if(e.dom.value == v){
23376                 e.dom.checked = true;
23377             }
23378         });
23379         
23380         if(suppressEvent !== true){
23381             this.fireEvent('check', this, true);
23382         }
23383
23384         this.validate();
23385         
23386         return;
23387     },
23388     
23389     validate : function()
23390     {
23391         if(this.getVisibilityEl().hasClass('hidden')){
23392             return true;
23393         }
23394         
23395         if(
23396                 this.disabled || 
23397                 (this.inputType == 'radio' && this.validateRadio()) ||
23398                 (this.inputType == 'checkbox' && this.validateCheckbox())
23399         ){
23400             this.markValid();
23401             return true;
23402         }
23403         
23404         this.markInvalid();
23405         return false;
23406     },
23407     
23408     validateRadio : function()
23409     {
23410         if(this.getVisibilityEl().hasClass('hidden')){
23411             return true;
23412         }
23413         
23414         if(this.allowBlank){
23415             return true;
23416         }
23417         
23418         var valid = false;
23419         
23420         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23421             if(!e.dom.checked){
23422                 return;
23423             }
23424             
23425             valid = true;
23426             
23427             return false;
23428         });
23429         
23430         return valid;
23431     },
23432     
23433     validateCheckbox : function()
23434     {
23435         if(!this.groupId){
23436             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23437             //return (this.getValue() == this.inputValue) ? true : false;
23438         }
23439         
23440         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23441         
23442         if(!group){
23443             return false;
23444         }
23445         
23446         var r = false;
23447         
23448         for(var i in group){
23449             if(group[i].el.isVisible(true)){
23450                 r = false;
23451                 break;
23452             }
23453             
23454             r = true;
23455         }
23456         
23457         for(var i in group){
23458             if(r){
23459                 break;
23460             }
23461             
23462             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23463         }
23464         
23465         return r;
23466     },
23467     
23468     /**
23469      * Mark this field as valid
23470      */
23471     markValid : function()
23472     {
23473         var _this = this;
23474         
23475         this.fireEvent('valid', this);
23476         
23477         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23478         
23479         if(this.groupId){
23480             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23481         }
23482         
23483         if(label){
23484             label.markValid();
23485         }
23486
23487         if(this.inputType == 'radio'){
23488             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23489                 var fg = e.findParent('.form-group', false, true);
23490                 if (Roo.bootstrap.version == 3) {
23491                     fg.removeClass([_this.invalidClass, _this.validClass]);
23492                     fg.addClass(_this.validClass);
23493                 } else {
23494                     fg.removeClass(['is-valid', 'is-invalid']);
23495                     fg.addClass('is-valid');
23496                 }
23497             });
23498             
23499             return;
23500         }
23501
23502         if(!this.groupId){
23503             var fg = this.el.findParent('.form-group', false, true);
23504             if (Roo.bootstrap.version == 3) {
23505                 fg.removeClass([this.invalidClass, this.validClass]);
23506                 fg.addClass(this.validClass);
23507             } else {
23508                 fg.removeClass(['is-valid', 'is-invalid']);
23509                 fg.addClass('is-valid');
23510             }
23511             return;
23512         }
23513         
23514         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23515         
23516         if(!group){
23517             return;
23518         }
23519         
23520         for(var i in group){
23521             var fg = group[i].el.findParent('.form-group', false, true);
23522             if (Roo.bootstrap.version == 3) {
23523                 fg.removeClass([this.invalidClass, this.validClass]);
23524                 fg.addClass(this.validClass);
23525             } else {
23526                 fg.removeClass(['is-valid', 'is-invalid']);
23527                 fg.addClass('is-valid');
23528             }
23529         }
23530     },
23531     
23532      /**
23533      * Mark this field as invalid
23534      * @param {String} msg The validation message
23535      */
23536     markInvalid : function(msg)
23537     {
23538         if(this.allowBlank){
23539             return;
23540         }
23541         
23542         var _this = this;
23543         
23544         this.fireEvent('invalid', this, msg);
23545         
23546         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23547         
23548         if(this.groupId){
23549             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23550         }
23551         
23552         if(label){
23553             label.markInvalid();
23554         }
23555             
23556         if(this.inputType == 'radio'){
23557             
23558             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23559                 var fg = e.findParent('.form-group', false, true);
23560                 if (Roo.bootstrap.version == 3) {
23561                     fg.removeClass([_this.invalidClass, _this.validClass]);
23562                     fg.addClass(_this.invalidClass);
23563                 } else {
23564                     fg.removeClass(['is-invalid', 'is-valid']);
23565                     fg.addClass('is-invalid');
23566                 }
23567             });
23568             
23569             return;
23570         }
23571         
23572         if(!this.groupId){
23573             var fg = this.el.findParent('.form-group', false, true);
23574             if (Roo.bootstrap.version == 3) {
23575                 fg.removeClass([_this.invalidClass, _this.validClass]);
23576                 fg.addClass(_this.invalidClass);
23577             } else {
23578                 fg.removeClass(['is-invalid', 'is-valid']);
23579                 fg.addClass('is-invalid');
23580             }
23581             return;
23582         }
23583         
23584         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23585         
23586         if(!group){
23587             return;
23588         }
23589         
23590         for(var i in group){
23591             var fg = group[i].el.findParent('.form-group', false, true);
23592             if (Roo.bootstrap.version == 3) {
23593                 fg.removeClass([_this.invalidClass, _this.validClass]);
23594                 fg.addClass(_this.invalidClass);
23595             } else {
23596                 fg.removeClass(['is-invalid', 'is-valid']);
23597                 fg.addClass('is-invalid');
23598             }
23599         }
23600         
23601     },
23602     
23603     clearInvalid : function()
23604     {
23605         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23606         
23607         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23608         
23609         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23610         
23611         if (label && label.iconEl) {
23612             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23613             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23614         }
23615     },
23616     
23617     disable : function()
23618     {
23619         if(this.inputType != 'radio'){
23620             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23621             return;
23622         }
23623         
23624         var _this = this;
23625         
23626         if(this.rendered){
23627             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23628                 _this.getActionEl().addClass(this.disabledClass);
23629                 e.dom.disabled = true;
23630             });
23631         }
23632         
23633         this.disabled = true;
23634         this.fireEvent("disable", this);
23635         return this;
23636     },
23637
23638     enable : function()
23639     {
23640         if(this.inputType != 'radio'){
23641             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23642             return;
23643         }
23644         
23645         var _this = this;
23646         
23647         if(this.rendered){
23648             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23649                 _this.getActionEl().removeClass(this.disabledClass);
23650                 e.dom.disabled = false;
23651             });
23652         }
23653         
23654         this.disabled = false;
23655         this.fireEvent("enable", this);
23656         return this;
23657     },
23658     
23659     setBoxLabel : function(v)
23660     {
23661         this.boxLabel = v;
23662         
23663         if(this.rendered){
23664             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23665         }
23666     }
23667
23668 });
23669
23670 Roo.apply(Roo.bootstrap.CheckBox, {
23671     
23672     groups: {},
23673     
23674      /**
23675     * register a CheckBox Group
23676     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23677     */
23678     register : function(checkbox)
23679     {
23680         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23681             this.groups[checkbox.groupId] = {};
23682         }
23683         
23684         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23685             return;
23686         }
23687         
23688         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23689         
23690     },
23691     /**
23692     * fetch a CheckBox Group based on the group ID
23693     * @param {string} the group ID
23694     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23695     */
23696     get: function(groupId) {
23697         if (typeof(this.groups[groupId]) == 'undefined') {
23698             return false;
23699         }
23700         
23701         return this.groups[groupId] ;
23702     }
23703     
23704     
23705 });
23706 /*
23707  * - LGPL
23708  *
23709  * RadioItem
23710  * 
23711  */
23712
23713 /**
23714  * @class Roo.bootstrap.Radio
23715  * @extends Roo.bootstrap.Component
23716  * Bootstrap Radio class
23717  * @cfg {String} boxLabel - the label associated
23718  * @cfg {String} value - the value of radio
23719  * 
23720  * @constructor
23721  * Create a new Radio
23722  * @param {Object} config The config object
23723  */
23724 Roo.bootstrap.Radio = function(config){
23725     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23726     
23727 };
23728
23729 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23730     
23731     boxLabel : '',
23732     
23733     value : '',
23734     
23735     getAutoCreate : function()
23736     {
23737         var cfg = {
23738             tag : 'div',
23739             cls : 'form-group radio',
23740             cn : [
23741                 {
23742                     tag : 'label',
23743                     cls : 'box-label',
23744                     html : this.boxLabel
23745                 }
23746             ]
23747         };
23748         
23749         return cfg;
23750     },
23751     
23752     initEvents : function() 
23753     {
23754         this.parent().register(this);
23755         
23756         this.el.on('click', this.onClick, this);
23757         
23758     },
23759     
23760     onClick : function(e)
23761     {
23762         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23763             this.setChecked(true);
23764         }
23765     },
23766     
23767     setChecked : function(state, suppressEvent)
23768     {
23769         this.parent().setValue(this.value, suppressEvent);
23770         
23771     },
23772     
23773     setBoxLabel : function(v)
23774     {
23775         this.boxLabel = v;
23776         
23777         if(this.rendered){
23778             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23779         }
23780     }
23781     
23782 });
23783  
23784
23785  /*
23786  * - LGPL
23787  *
23788  * Input
23789  * 
23790  */
23791
23792 /**
23793  * @class Roo.bootstrap.SecurePass
23794  * @extends Roo.bootstrap.Input
23795  * Bootstrap SecurePass class
23796  *
23797  * 
23798  * @constructor
23799  * Create a new SecurePass
23800  * @param {Object} config The config object
23801  */
23802  
23803 Roo.bootstrap.SecurePass = function (config) {
23804     // these go here, so the translation tool can replace them..
23805     this.errors = {
23806         PwdEmpty: "Please type a password, and then retype it to confirm.",
23807         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23808         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23809         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23810         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23811         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23812         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23813         TooWeak: "Your password is Too Weak."
23814     },
23815     this.meterLabel = "Password strength:";
23816     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23817     this.meterClass = [
23818         "roo-password-meter-tooweak", 
23819         "roo-password-meter-weak", 
23820         "roo-password-meter-medium", 
23821         "roo-password-meter-strong", 
23822         "roo-password-meter-grey"
23823     ];
23824     
23825     this.errors = {};
23826     
23827     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23828 }
23829
23830 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23831     /**
23832      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23833      * {
23834      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23835      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23836      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23837      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23838      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23839      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23840      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23841      * })
23842      */
23843     // private
23844     
23845     meterWidth: 300,
23846     errorMsg :'',    
23847     errors: false,
23848     imageRoot: '/',
23849     /**
23850      * @cfg {String/Object} Label for the strength meter (defaults to
23851      * 'Password strength:')
23852      */
23853     // private
23854     meterLabel: '',
23855     /**
23856      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23857      * ['Weak', 'Medium', 'Strong'])
23858      */
23859     // private    
23860     pwdStrengths: false,    
23861     // private
23862     strength: 0,
23863     // private
23864     _lastPwd: null,
23865     // private
23866     kCapitalLetter: 0,
23867     kSmallLetter: 1,
23868     kDigit: 2,
23869     kPunctuation: 3,
23870     
23871     insecure: false,
23872     // private
23873     initEvents: function ()
23874     {
23875         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23876
23877         if (this.el.is('input[type=password]') && Roo.isSafari) {
23878             this.el.on('keydown', this.SafariOnKeyDown, this);
23879         }
23880
23881         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23882     },
23883     // private
23884     onRender: function (ct, position)
23885     {
23886         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23887         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23888         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23889
23890         this.trigger.createChild({
23891                    cn: [
23892                     {
23893                     //id: 'PwdMeter',
23894                     tag: 'div',
23895                     cls: 'roo-password-meter-grey col-xs-12',
23896                     style: {
23897                         //width: 0,
23898                         //width: this.meterWidth + 'px'                                                
23899                         }
23900                     },
23901                     {                            
23902                          cls: 'roo-password-meter-text'                          
23903                     }
23904                 ]            
23905         });
23906
23907          
23908         if (this.hideTrigger) {
23909             this.trigger.setDisplayed(false);
23910         }
23911         this.setSize(this.width || '', this.height || '');
23912     },
23913     // private
23914     onDestroy: function ()
23915     {
23916         if (this.trigger) {
23917             this.trigger.removeAllListeners();
23918             this.trigger.remove();
23919         }
23920         if (this.wrap) {
23921             this.wrap.remove();
23922         }
23923         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23924     },
23925     // private
23926     checkStrength: function ()
23927     {
23928         var pwd = this.inputEl().getValue();
23929         if (pwd == this._lastPwd) {
23930             return;
23931         }
23932
23933         var strength;
23934         if (this.ClientSideStrongPassword(pwd)) {
23935             strength = 3;
23936         } else if (this.ClientSideMediumPassword(pwd)) {
23937             strength = 2;
23938         } else if (this.ClientSideWeakPassword(pwd)) {
23939             strength = 1;
23940         } else {
23941             strength = 0;
23942         }
23943         
23944         Roo.log('strength1: ' + strength);
23945         
23946         //var pm = this.trigger.child('div/div/div').dom;
23947         var pm = this.trigger.child('div/div');
23948         pm.removeClass(this.meterClass);
23949         pm.addClass(this.meterClass[strength]);
23950                 
23951         
23952         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23953                 
23954         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23955         
23956         this._lastPwd = pwd;
23957     },
23958     reset: function ()
23959     {
23960         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23961         
23962         this._lastPwd = '';
23963         
23964         var pm = this.trigger.child('div/div');
23965         pm.removeClass(this.meterClass);
23966         pm.addClass('roo-password-meter-grey');        
23967         
23968         
23969         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23970         
23971         pt.innerHTML = '';
23972         this.inputEl().dom.type='password';
23973     },
23974     // private
23975     validateValue: function (value)
23976     {
23977         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23978             return false;
23979         }
23980         if (value.length == 0) {
23981             if (this.allowBlank) {
23982                 this.clearInvalid();
23983                 return true;
23984             }
23985
23986             this.markInvalid(this.errors.PwdEmpty);
23987             this.errorMsg = this.errors.PwdEmpty;
23988             return false;
23989         }
23990         
23991         if(this.insecure){
23992             return true;
23993         }
23994         
23995         if (!value.match(/[\x21-\x7e]+/)) {
23996             this.markInvalid(this.errors.PwdBadChar);
23997             this.errorMsg = this.errors.PwdBadChar;
23998             return false;
23999         }
24000         if (value.length < 6) {
24001             this.markInvalid(this.errors.PwdShort);
24002             this.errorMsg = this.errors.PwdShort;
24003             return false;
24004         }
24005         if (value.length > 16) {
24006             this.markInvalid(this.errors.PwdLong);
24007             this.errorMsg = this.errors.PwdLong;
24008             return false;
24009         }
24010         var strength;
24011         if (this.ClientSideStrongPassword(value)) {
24012             strength = 3;
24013         } else if (this.ClientSideMediumPassword(value)) {
24014             strength = 2;
24015         } else if (this.ClientSideWeakPassword(value)) {
24016             strength = 1;
24017         } else {
24018             strength = 0;
24019         }
24020
24021         
24022         if (strength < 2) {
24023             //this.markInvalid(this.errors.TooWeak);
24024             this.errorMsg = this.errors.TooWeak;
24025             //return false;
24026         }
24027         
24028         
24029         console.log('strength2: ' + strength);
24030         
24031         //var pm = this.trigger.child('div/div/div').dom;
24032         
24033         var pm = this.trigger.child('div/div');
24034         pm.removeClass(this.meterClass);
24035         pm.addClass(this.meterClass[strength]);
24036                 
24037         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24038                 
24039         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24040         
24041         this.errorMsg = ''; 
24042         return true;
24043     },
24044     // private
24045     CharacterSetChecks: function (type)
24046     {
24047         this.type = type;
24048         this.fResult = false;
24049     },
24050     // private
24051     isctype: function (character, type)
24052     {
24053         switch (type) {  
24054             case this.kCapitalLetter:
24055                 if (character >= 'A' && character <= 'Z') {
24056                     return true;
24057                 }
24058                 break;
24059             
24060             case this.kSmallLetter:
24061                 if (character >= 'a' && character <= 'z') {
24062                     return true;
24063                 }
24064                 break;
24065             
24066             case this.kDigit:
24067                 if (character >= '0' && character <= '9') {
24068                     return true;
24069                 }
24070                 break;
24071             
24072             case this.kPunctuation:
24073                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24074                     return true;
24075                 }
24076                 break;
24077             
24078             default:
24079                 return false;
24080         }
24081
24082     },
24083     // private
24084     IsLongEnough: function (pwd, size)
24085     {
24086         return !(pwd == null || isNaN(size) || pwd.length < size);
24087     },
24088     // private
24089     SpansEnoughCharacterSets: function (word, nb)
24090     {
24091         if (!this.IsLongEnough(word, nb))
24092         {
24093             return false;
24094         }
24095
24096         var characterSetChecks = new Array(
24097             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24098             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24099         );
24100         
24101         for (var index = 0; index < word.length; ++index) {
24102             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24103                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24104                     characterSetChecks[nCharSet].fResult = true;
24105                     break;
24106                 }
24107             }
24108         }
24109
24110         var nCharSets = 0;
24111         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24112             if (characterSetChecks[nCharSet].fResult) {
24113                 ++nCharSets;
24114             }
24115         }
24116
24117         if (nCharSets < nb) {
24118             return false;
24119         }
24120         return true;
24121     },
24122     // private
24123     ClientSideStrongPassword: function (pwd)
24124     {
24125         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24126     },
24127     // private
24128     ClientSideMediumPassword: function (pwd)
24129     {
24130         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24131     },
24132     // private
24133     ClientSideWeakPassword: function (pwd)
24134     {
24135         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24136     }
24137           
24138 })//<script type="text/javascript">
24139
24140 /*
24141  * Based  Ext JS Library 1.1.1
24142  * Copyright(c) 2006-2007, Ext JS, LLC.
24143  * LGPL
24144  *
24145  */
24146  
24147 /**
24148  * @class Roo.HtmlEditorCore
24149  * @extends Roo.Component
24150  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24151  *
24152  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24153  */
24154
24155 Roo.HtmlEditorCore = function(config){
24156     
24157     
24158     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24159     
24160     
24161     this.addEvents({
24162         /**
24163          * @event initialize
24164          * Fires when the editor is fully initialized (including the iframe)
24165          * @param {Roo.HtmlEditorCore} this
24166          */
24167         initialize: true,
24168         /**
24169          * @event activate
24170          * Fires when the editor is first receives the focus. Any insertion must wait
24171          * until after this event.
24172          * @param {Roo.HtmlEditorCore} this
24173          */
24174         activate: true,
24175          /**
24176          * @event beforesync
24177          * Fires before the textarea is updated with content from the editor iframe. Return false
24178          * to cancel the sync.
24179          * @param {Roo.HtmlEditorCore} this
24180          * @param {String} html
24181          */
24182         beforesync: true,
24183          /**
24184          * @event beforepush
24185          * Fires before the iframe editor is updated with content from the textarea. Return false
24186          * to cancel the push.
24187          * @param {Roo.HtmlEditorCore} this
24188          * @param {String} html
24189          */
24190         beforepush: true,
24191          /**
24192          * @event sync
24193          * Fires when the textarea is updated with content from the editor iframe.
24194          * @param {Roo.HtmlEditorCore} this
24195          * @param {String} html
24196          */
24197         sync: true,
24198          /**
24199          * @event push
24200          * Fires when the iframe editor is updated with content from the textarea.
24201          * @param {Roo.HtmlEditorCore} this
24202          * @param {String} html
24203          */
24204         push: true,
24205         
24206         /**
24207          * @event editorevent
24208          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24209          * @param {Roo.HtmlEditorCore} this
24210          */
24211         editorevent: true
24212         
24213     });
24214     
24215     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24216     
24217     // defaults : white / black...
24218     this.applyBlacklists();
24219     
24220     
24221     
24222 };
24223
24224
24225 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24226
24227
24228      /**
24229      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24230      */
24231     
24232     owner : false,
24233     
24234      /**
24235      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24236      *                        Roo.resizable.
24237      */
24238     resizable : false,
24239      /**
24240      * @cfg {Number} height (in pixels)
24241      */   
24242     height: 300,
24243    /**
24244      * @cfg {Number} width (in pixels)
24245      */   
24246     width: 500,
24247     
24248     /**
24249      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24250      * 
24251      */
24252     stylesheets: false,
24253     
24254     // id of frame..
24255     frameId: false,
24256     
24257     // private properties
24258     validationEvent : false,
24259     deferHeight: true,
24260     initialized : false,
24261     activated : false,
24262     sourceEditMode : false,
24263     onFocus : Roo.emptyFn,
24264     iframePad:3,
24265     hideMode:'offsets',
24266     
24267     clearUp: true,
24268     
24269     // blacklist + whitelisted elements..
24270     black: false,
24271     white: false,
24272      
24273     bodyCls : '',
24274
24275     /**
24276      * Protected method that will not generally be called directly. It
24277      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24278      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24279      */
24280     getDocMarkup : function(){
24281         // body styles..
24282         var st = '';
24283         
24284         // inherit styels from page...?? 
24285         if (this.stylesheets === false) {
24286             
24287             Roo.get(document.head).select('style').each(function(node) {
24288                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24289             });
24290             
24291             Roo.get(document.head).select('link').each(function(node) { 
24292                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24293             });
24294             
24295         } else if (!this.stylesheets.length) {
24296                 // simple..
24297                 st = '<style type="text/css">' +
24298                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24299                    '</style>';
24300         } else {
24301             for (var i in this.stylesheets) { 
24302                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24303             }
24304             
24305         }
24306         
24307         st +=  '<style type="text/css">' +
24308             'IMG { cursor: pointer } ' +
24309         '</style>';
24310
24311         var cls = 'roo-htmleditor-body';
24312         
24313         if(this.bodyCls.length){
24314             cls += ' ' + this.bodyCls;
24315         }
24316         
24317         return '<html><head>' + st  +
24318             //<style type="text/css">' +
24319             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24320             //'</style>' +
24321             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24322     },
24323
24324     // private
24325     onRender : function(ct, position)
24326     {
24327         var _t = this;
24328         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24329         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24330         
24331         
24332         this.el.dom.style.border = '0 none';
24333         this.el.dom.setAttribute('tabIndex', -1);
24334         this.el.addClass('x-hidden hide');
24335         
24336         
24337         
24338         if(Roo.isIE){ // fix IE 1px bogus margin
24339             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24340         }
24341        
24342         
24343         this.frameId = Roo.id();
24344         
24345          
24346         
24347         var iframe = this.owner.wrap.createChild({
24348             tag: 'iframe',
24349             cls: 'form-control', // bootstrap..
24350             id: this.frameId,
24351             name: this.frameId,
24352             frameBorder : 'no',
24353             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24354         }, this.el
24355         );
24356         
24357         
24358         this.iframe = iframe.dom;
24359
24360          this.assignDocWin();
24361         
24362         this.doc.designMode = 'on';
24363        
24364         this.doc.open();
24365         this.doc.write(this.getDocMarkup());
24366         this.doc.close();
24367
24368         
24369         var task = { // must defer to wait for browser to be ready
24370             run : function(){
24371                 //console.log("run task?" + this.doc.readyState);
24372                 this.assignDocWin();
24373                 if(this.doc.body || this.doc.readyState == 'complete'){
24374                     try {
24375                         this.doc.designMode="on";
24376                     } catch (e) {
24377                         return;
24378                     }
24379                     Roo.TaskMgr.stop(task);
24380                     this.initEditor.defer(10, this);
24381                 }
24382             },
24383             interval : 10,
24384             duration: 10000,
24385             scope: this
24386         };
24387         Roo.TaskMgr.start(task);
24388
24389     },
24390
24391     // private
24392     onResize : function(w, h)
24393     {
24394          Roo.log('resize: ' +w + ',' + h );
24395         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24396         if(!this.iframe){
24397             return;
24398         }
24399         if(typeof w == 'number'){
24400             
24401             this.iframe.style.width = w + 'px';
24402         }
24403         if(typeof h == 'number'){
24404             
24405             this.iframe.style.height = h + 'px';
24406             if(this.doc){
24407                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24408             }
24409         }
24410         
24411     },
24412
24413     /**
24414      * Toggles the editor between standard and source edit mode.
24415      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24416      */
24417     toggleSourceEdit : function(sourceEditMode){
24418         
24419         this.sourceEditMode = sourceEditMode === true;
24420         
24421         if(this.sourceEditMode){
24422  
24423             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24424             
24425         }else{
24426             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24427             //this.iframe.className = '';
24428             this.deferFocus();
24429         }
24430         //this.setSize(this.owner.wrap.getSize());
24431         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24432     },
24433
24434     
24435   
24436
24437     /**
24438      * Protected method that will not generally be called directly. If you need/want
24439      * custom HTML cleanup, this is the method you should override.
24440      * @param {String} html The HTML to be cleaned
24441      * return {String} The cleaned HTML
24442      */
24443     cleanHtml : function(html){
24444         html = String(html);
24445         if(html.length > 5){
24446             if(Roo.isSafari){ // strip safari nonsense
24447                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24448             }
24449         }
24450         if(html == '&nbsp;'){
24451             html = '';
24452         }
24453         return html;
24454     },
24455
24456     /**
24457      * HTML Editor -> Textarea
24458      * Protected method that will not generally be called directly. Syncs the contents
24459      * of the editor iframe with the textarea.
24460      */
24461     syncValue : function(){
24462         if(this.initialized){
24463             var bd = (this.doc.body || this.doc.documentElement);
24464             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24465             var html = bd.innerHTML;
24466             if(Roo.isSafari){
24467                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24468                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24469                 if(m && m[1]){
24470                     html = '<div style="'+m[0]+'">' + html + '</div>';
24471                 }
24472             }
24473             html = this.cleanHtml(html);
24474             // fix up the special chars.. normaly like back quotes in word...
24475             // however we do not want to do this with chinese..
24476             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24477                 
24478                 var cc = match.charCodeAt();
24479
24480                 // Get the character value, handling surrogate pairs
24481                 if (match.length == 2) {
24482                     // It's a surrogate pair, calculate the Unicode code point
24483                     var high = match.charCodeAt(0) - 0xD800;
24484                     var low  = match.charCodeAt(1) - 0xDC00;
24485                     cc = (high * 0x400) + low + 0x10000;
24486                 }  else if (
24487                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24488                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24489                     (cc >= 0xf900 && cc < 0xfb00 )
24490                 ) {
24491                         return match;
24492                 }  
24493          
24494                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24495                 return "&#" + cc + ";";
24496                 
24497                 
24498             });
24499             
24500             
24501              
24502             if(this.owner.fireEvent('beforesync', this, html) !== false){
24503                 this.el.dom.value = html;
24504                 this.owner.fireEvent('sync', this, html);
24505             }
24506         }
24507     },
24508
24509     /**
24510      * Protected method that will not generally be called directly. Pushes the value of the textarea
24511      * into the iframe editor.
24512      */
24513     pushValue : function(){
24514         if(this.initialized){
24515             var v = this.el.dom.value.trim();
24516             
24517 //            if(v.length < 1){
24518 //                v = '&#160;';
24519 //            }
24520             
24521             if(this.owner.fireEvent('beforepush', this, v) !== false){
24522                 var d = (this.doc.body || this.doc.documentElement);
24523                 d.innerHTML = v;
24524                 this.cleanUpPaste();
24525                 this.el.dom.value = d.innerHTML;
24526                 this.owner.fireEvent('push', this, v);
24527             }
24528         }
24529     },
24530
24531     // private
24532     deferFocus : function(){
24533         this.focus.defer(10, this);
24534     },
24535
24536     // doc'ed in Field
24537     focus : function(){
24538         if(this.win && !this.sourceEditMode){
24539             this.win.focus();
24540         }else{
24541             this.el.focus();
24542         }
24543     },
24544     
24545     assignDocWin: function()
24546     {
24547         var iframe = this.iframe;
24548         
24549          if(Roo.isIE){
24550             this.doc = iframe.contentWindow.document;
24551             this.win = iframe.contentWindow;
24552         } else {
24553 //            if (!Roo.get(this.frameId)) {
24554 //                return;
24555 //            }
24556 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24557 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24558             
24559             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24560                 return;
24561             }
24562             
24563             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24564             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24565         }
24566     },
24567     
24568     // private
24569     initEditor : function(){
24570         //console.log("INIT EDITOR");
24571         this.assignDocWin();
24572         
24573         
24574         
24575         this.doc.designMode="on";
24576         this.doc.open();
24577         this.doc.write(this.getDocMarkup());
24578         this.doc.close();
24579         
24580         var dbody = (this.doc.body || this.doc.documentElement);
24581         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24582         // this copies styles from the containing element into thsi one..
24583         // not sure why we need all of this..
24584         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24585         
24586         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24587         //ss['background-attachment'] = 'fixed'; // w3c
24588         dbody.bgProperties = 'fixed'; // ie
24589         //Roo.DomHelper.applyStyles(dbody, ss);
24590         Roo.EventManager.on(this.doc, {
24591             //'mousedown': this.onEditorEvent,
24592             'mouseup': this.onEditorEvent,
24593             'dblclick': this.onEditorEvent,
24594             'click': this.onEditorEvent,
24595             'keyup': this.onEditorEvent,
24596             buffer:100,
24597             scope: this
24598         });
24599         if(Roo.isGecko){
24600             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24601         }
24602         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24603             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24604         }
24605         this.initialized = true;
24606
24607         this.owner.fireEvent('initialize', this);
24608         this.pushValue();
24609     },
24610
24611     // private
24612     onDestroy : function(){
24613         
24614         
24615         
24616         if(this.rendered){
24617             
24618             //for (var i =0; i < this.toolbars.length;i++) {
24619             //    // fixme - ask toolbars for heights?
24620             //    this.toolbars[i].onDestroy();
24621            // }
24622             
24623             //this.wrap.dom.innerHTML = '';
24624             //this.wrap.remove();
24625         }
24626     },
24627
24628     // private
24629     onFirstFocus : function(){
24630         
24631         this.assignDocWin();
24632         
24633         
24634         this.activated = true;
24635          
24636     
24637         if(Roo.isGecko){ // prevent silly gecko errors
24638             this.win.focus();
24639             var s = this.win.getSelection();
24640             if(!s.focusNode || s.focusNode.nodeType != 3){
24641                 var r = s.getRangeAt(0);
24642                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24643                 r.collapse(true);
24644                 this.deferFocus();
24645             }
24646             try{
24647                 this.execCmd('useCSS', true);
24648                 this.execCmd('styleWithCSS', false);
24649             }catch(e){}
24650         }
24651         this.owner.fireEvent('activate', this);
24652     },
24653
24654     // private
24655     adjustFont: function(btn){
24656         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24657         //if(Roo.isSafari){ // safari
24658         //    adjust *= 2;
24659        // }
24660         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24661         if(Roo.isSafari){ // safari
24662             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24663             v =  (v < 10) ? 10 : v;
24664             v =  (v > 48) ? 48 : v;
24665             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24666             
24667         }
24668         
24669         
24670         v = Math.max(1, v+adjust);
24671         
24672         this.execCmd('FontSize', v  );
24673     },
24674
24675     onEditorEvent : function(e)
24676     {
24677         this.owner.fireEvent('editorevent', this, e);
24678       //  this.updateToolbar();
24679         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24680     },
24681
24682     insertTag : function(tg)
24683     {
24684         // could be a bit smarter... -> wrap the current selected tRoo..
24685         if (tg.toLowerCase() == 'span' ||
24686             tg.toLowerCase() == 'code' ||
24687             tg.toLowerCase() == 'sup' ||
24688             tg.toLowerCase() == 'sub' 
24689             ) {
24690             
24691             range = this.createRange(this.getSelection());
24692             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24693             wrappingNode.appendChild(range.extractContents());
24694             range.insertNode(wrappingNode);
24695
24696             return;
24697             
24698             
24699             
24700         }
24701         this.execCmd("formatblock",   tg);
24702         
24703     },
24704     
24705     insertText : function(txt)
24706     {
24707         
24708         
24709         var range = this.createRange();
24710         range.deleteContents();
24711                //alert(Sender.getAttribute('label'));
24712                
24713         range.insertNode(this.doc.createTextNode(txt));
24714     } ,
24715     
24716      
24717
24718     /**
24719      * Executes a Midas editor command on the editor document and performs necessary focus and
24720      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24721      * @param {String} cmd The Midas command
24722      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24723      */
24724     relayCmd : function(cmd, value){
24725         this.win.focus();
24726         this.execCmd(cmd, value);
24727         this.owner.fireEvent('editorevent', this);
24728         //this.updateToolbar();
24729         this.owner.deferFocus();
24730     },
24731
24732     /**
24733      * Executes a Midas editor command directly on the editor document.
24734      * For visual commands, you should use {@link #relayCmd} instead.
24735      * <b>This should only be called after the editor is initialized.</b>
24736      * @param {String} cmd The Midas command
24737      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24738      */
24739     execCmd : function(cmd, value){
24740         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24741         this.syncValue();
24742     },
24743  
24744  
24745    
24746     /**
24747      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24748      * to insert tRoo.
24749      * @param {String} text | dom node.. 
24750      */
24751     insertAtCursor : function(text)
24752     {
24753         
24754         if(!this.activated){
24755             return;
24756         }
24757         /*
24758         if(Roo.isIE){
24759             this.win.focus();
24760             var r = this.doc.selection.createRange();
24761             if(r){
24762                 r.collapse(true);
24763                 r.pasteHTML(text);
24764                 this.syncValue();
24765                 this.deferFocus();
24766             
24767             }
24768             return;
24769         }
24770         */
24771         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24772             this.win.focus();
24773             
24774             
24775             // from jquery ui (MIT licenced)
24776             var range, node;
24777             var win = this.win;
24778             
24779             if (win.getSelection && win.getSelection().getRangeAt) {
24780                 range = win.getSelection().getRangeAt(0);
24781                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24782                 range.insertNode(node);
24783             } else if (win.document.selection && win.document.selection.createRange) {
24784                 // no firefox support
24785                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24786                 win.document.selection.createRange().pasteHTML(txt);
24787             } else {
24788                 // no firefox support
24789                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24790                 this.execCmd('InsertHTML', txt);
24791             } 
24792             
24793             this.syncValue();
24794             
24795             this.deferFocus();
24796         }
24797     },
24798  // private
24799     mozKeyPress : function(e){
24800         if(e.ctrlKey){
24801             var c = e.getCharCode(), cmd;
24802           
24803             if(c > 0){
24804                 c = String.fromCharCode(c).toLowerCase();
24805                 switch(c){
24806                     case 'b':
24807                         cmd = 'bold';
24808                         break;
24809                     case 'i':
24810                         cmd = 'italic';
24811                         break;
24812                     
24813                     case 'u':
24814                         cmd = 'underline';
24815                         break;
24816                     
24817                     case 'v':
24818                         this.cleanUpPaste.defer(100, this);
24819                         return;
24820                         
24821                 }
24822                 if(cmd){
24823                     this.win.focus();
24824                     this.execCmd(cmd);
24825                     this.deferFocus();
24826                     e.preventDefault();
24827                 }
24828                 
24829             }
24830         }
24831     },
24832
24833     // private
24834     fixKeys : function(){ // load time branching for fastest keydown performance
24835         if(Roo.isIE){
24836             return function(e){
24837                 var k = e.getKey(), r;
24838                 if(k == e.TAB){
24839                     e.stopEvent();
24840                     r = this.doc.selection.createRange();
24841                     if(r){
24842                         r.collapse(true);
24843                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24844                         this.deferFocus();
24845                     }
24846                     return;
24847                 }
24848                 
24849                 if(k == e.ENTER){
24850                     r = this.doc.selection.createRange();
24851                     if(r){
24852                         var target = r.parentElement();
24853                         if(!target || target.tagName.toLowerCase() != 'li'){
24854                             e.stopEvent();
24855                             r.pasteHTML('<br />');
24856                             r.collapse(false);
24857                             r.select();
24858                         }
24859                     }
24860                 }
24861                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24862                     this.cleanUpPaste.defer(100, this);
24863                     return;
24864                 }
24865                 
24866                 
24867             };
24868         }else if(Roo.isOpera){
24869             return function(e){
24870                 var k = e.getKey();
24871                 if(k == e.TAB){
24872                     e.stopEvent();
24873                     this.win.focus();
24874                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24875                     this.deferFocus();
24876                 }
24877                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24878                     this.cleanUpPaste.defer(100, this);
24879                     return;
24880                 }
24881                 
24882             };
24883         }else if(Roo.isSafari){
24884             return function(e){
24885                 var k = e.getKey();
24886                 
24887                 if(k == e.TAB){
24888                     e.stopEvent();
24889                     this.execCmd('InsertText','\t');
24890                     this.deferFocus();
24891                     return;
24892                 }
24893                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24894                     this.cleanUpPaste.defer(100, this);
24895                     return;
24896                 }
24897                 
24898              };
24899         }
24900     }(),
24901     
24902     getAllAncestors: function()
24903     {
24904         var p = this.getSelectedNode();
24905         var a = [];
24906         if (!p) {
24907             a.push(p); // push blank onto stack..
24908             p = this.getParentElement();
24909         }
24910         
24911         
24912         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24913             a.push(p);
24914             p = p.parentNode;
24915         }
24916         a.push(this.doc.body);
24917         return a;
24918     },
24919     lastSel : false,
24920     lastSelNode : false,
24921     
24922     
24923     getSelection : function() 
24924     {
24925         this.assignDocWin();
24926         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24927     },
24928     
24929     getSelectedNode: function() 
24930     {
24931         // this may only work on Gecko!!!
24932         
24933         // should we cache this!!!!
24934         
24935         
24936         
24937          
24938         var range = this.createRange(this.getSelection()).cloneRange();
24939         
24940         if (Roo.isIE) {
24941             var parent = range.parentElement();
24942             while (true) {
24943                 var testRange = range.duplicate();
24944                 testRange.moveToElementText(parent);
24945                 if (testRange.inRange(range)) {
24946                     break;
24947                 }
24948                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24949                     break;
24950                 }
24951                 parent = parent.parentElement;
24952             }
24953             return parent;
24954         }
24955         
24956         // is ancestor a text element.
24957         var ac =  range.commonAncestorContainer;
24958         if (ac.nodeType == 3) {
24959             ac = ac.parentNode;
24960         }
24961         
24962         var ar = ac.childNodes;
24963          
24964         var nodes = [];
24965         var other_nodes = [];
24966         var has_other_nodes = false;
24967         for (var i=0;i<ar.length;i++) {
24968             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24969                 continue;
24970             }
24971             // fullly contained node.
24972             
24973             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24974                 nodes.push(ar[i]);
24975                 continue;
24976             }
24977             
24978             // probably selected..
24979             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24980                 other_nodes.push(ar[i]);
24981                 continue;
24982             }
24983             // outer..
24984             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24985                 continue;
24986             }
24987             
24988             
24989             has_other_nodes = true;
24990         }
24991         if (!nodes.length && other_nodes.length) {
24992             nodes= other_nodes;
24993         }
24994         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24995             return false;
24996         }
24997         
24998         return nodes[0];
24999     },
25000     createRange: function(sel)
25001     {
25002         // this has strange effects when using with 
25003         // top toolbar - not sure if it's a great idea.
25004         //this.editor.contentWindow.focus();
25005         if (typeof sel != "undefined") {
25006             try {
25007                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25008             } catch(e) {
25009                 return this.doc.createRange();
25010             }
25011         } else {
25012             return this.doc.createRange();
25013         }
25014     },
25015     getParentElement: function()
25016     {
25017         
25018         this.assignDocWin();
25019         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25020         
25021         var range = this.createRange(sel);
25022          
25023         try {
25024             var p = range.commonAncestorContainer;
25025             while (p.nodeType == 3) { // text node
25026                 p = p.parentNode;
25027             }
25028             return p;
25029         } catch (e) {
25030             return null;
25031         }
25032     
25033     },
25034     /***
25035      *
25036      * Range intersection.. the hard stuff...
25037      *  '-1' = before
25038      *  '0' = hits..
25039      *  '1' = after.
25040      *         [ -- selected range --- ]
25041      *   [fail]                        [fail]
25042      *
25043      *    basically..
25044      *      if end is before start or  hits it. fail.
25045      *      if start is after end or hits it fail.
25046      *
25047      *   if either hits (but other is outside. - then it's not 
25048      *   
25049      *    
25050      **/
25051     
25052     
25053     // @see http://www.thismuchiknow.co.uk/?p=64.
25054     rangeIntersectsNode : function(range, node)
25055     {
25056         var nodeRange = node.ownerDocument.createRange();
25057         try {
25058             nodeRange.selectNode(node);
25059         } catch (e) {
25060             nodeRange.selectNodeContents(node);
25061         }
25062     
25063         var rangeStartRange = range.cloneRange();
25064         rangeStartRange.collapse(true);
25065     
25066         var rangeEndRange = range.cloneRange();
25067         rangeEndRange.collapse(false);
25068     
25069         var nodeStartRange = nodeRange.cloneRange();
25070         nodeStartRange.collapse(true);
25071     
25072         var nodeEndRange = nodeRange.cloneRange();
25073         nodeEndRange.collapse(false);
25074     
25075         return rangeStartRange.compareBoundaryPoints(
25076                  Range.START_TO_START, nodeEndRange) == -1 &&
25077                rangeEndRange.compareBoundaryPoints(
25078                  Range.START_TO_START, nodeStartRange) == 1;
25079         
25080          
25081     },
25082     rangeCompareNode : function(range, node)
25083     {
25084         var nodeRange = node.ownerDocument.createRange();
25085         try {
25086             nodeRange.selectNode(node);
25087         } catch (e) {
25088             nodeRange.selectNodeContents(node);
25089         }
25090         
25091         
25092         range.collapse(true);
25093     
25094         nodeRange.collapse(true);
25095      
25096         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25097         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25098          
25099         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25100         
25101         var nodeIsBefore   =  ss == 1;
25102         var nodeIsAfter    = ee == -1;
25103         
25104         if (nodeIsBefore && nodeIsAfter) {
25105             return 0; // outer
25106         }
25107         if (!nodeIsBefore && nodeIsAfter) {
25108             return 1; //right trailed.
25109         }
25110         
25111         if (nodeIsBefore && !nodeIsAfter) {
25112             return 2;  // left trailed.
25113         }
25114         // fully contined.
25115         return 3;
25116     },
25117
25118     // private? - in a new class?
25119     cleanUpPaste :  function()
25120     {
25121         // cleans up the whole document..
25122         Roo.log('cleanuppaste');
25123         
25124         this.cleanUpChildren(this.doc.body);
25125         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25126         if (clean != this.doc.body.innerHTML) {
25127             this.doc.body.innerHTML = clean;
25128         }
25129         
25130     },
25131     
25132     cleanWordChars : function(input) {// change the chars to hex code
25133         var he = Roo.HtmlEditorCore;
25134         
25135         var output = input;
25136         Roo.each(he.swapCodes, function(sw) { 
25137             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25138             
25139             output = output.replace(swapper, sw[1]);
25140         });
25141         
25142         return output;
25143     },
25144     
25145     
25146     cleanUpChildren : function (n)
25147     {
25148         if (!n.childNodes.length) {
25149             return;
25150         }
25151         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25152            this.cleanUpChild(n.childNodes[i]);
25153         }
25154     },
25155     
25156     
25157         
25158     
25159     cleanUpChild : function (node)
25160     {
25161         var ed = this;
25162         //console.log(node);
25163         if (node.nodeName == "#text") {
25164             // clean up silly Windows -- stuff?
25165             return; 
25166         }
25167         if (node.nodeName == "#comment") {
25168             node.parentNode.removeChild(node);
25169             // clean up silly Windows -- stuff?
25170             return; 
25171         }
25172         var lcname = node.tagName.toLowerCase();
25173         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25174         // whitelist of tags..
25175         
25176         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25177             // remove node.
25178             node.parentNode.removeChild(node);
25179             return;
25180             
25181         }
25182         
25183         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25184         
25185         // spans with no attributes - just remove them..
25186         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25187             remove_keep_children = true;
25188         }
25189         
25190         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25191         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25192         
25193         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25194         //    remove_keep_children = true;
25195         //}
25196         
25197         if (remove_keep_children) {
25198             this.cleanUpChildren(node);
25199             // inserts everything just before this node...
25200             while (node.childNodes.length) {
25201                 var cn = node.childNodes[0];
25202                 node.removeChild(cn);
25203                 node.parentNode.insertBefore(cn, node);
25204             }
25205             node.parentNode.removeChild(node);
25206             return;
25207         }
25208         
25209         if (!node.attributes || !node.attributes.length) {
25210             
25211           
25212             
25213             
25214             this.cleanUpChildren(node);
25215             return;
25216         }
25217         
25218         function cleanAttr(n,v)
25219         {
25220             
25221             if (v.match(/^\./) || v.match(/^\//)) {
25222                 return;
25223             }
25224             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25225                 return;
25226             }
25227             if (v.match(/^#/)) {
25228                 return;
25229             }
25230             if (v.match(/^\{/)) { // allow template editing.
25231                 return;
25232             }
25233 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25234             node.removeAttribute(n);
25235             
25236         }
25237         
25238         var cwhite = this.cwhite;
25239         var cblack = this.cblack;
25240             
25241         function cleanStyle(n,v)
25242         {
25243             if (v.match(/expression/)) { //XSS?? should we even bother..
25244                 node.removeAttribute(n);
25245                 return;
25246             }
25247             
25248             var parts = v.split(/;/);
25249             var clean = [];
25250             
25251             Roo.each(parts, function(p) {
25252                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25253                 if (!p.length) {
25254                     return true;
25255                 }
25256                 var l = p.split(':').shift().replace(/\s+/g,'');
25257                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25258                 
25259                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25260 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25261                     //node.removeAttribute(n);
25262                     return true;
25263                 }
25264                 //Roo.log()
25265                 // only allow 'c whitelisted system attributes'
25266                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25267 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25268                     //node.removeAttribute(n);
25269                     return true;
25270                 }
25271                 
25272                 
25273                  
25274                 
25275                 clean.push(p);
25276                 return true;
25277             });
25278             if (clean.length) { 
25279                 node.setAttribute(n, clean.join(';'));
25280             } else {
25281                 node.removeAttribute(n);
25282             }
25283             
25284         }
25285         
25286         
25287         for (var i = node.attributes.length-1; i > -1 ; i--) {
25288             var a = node.attributes[i];
25289             //console.log(a);
25290             
25291             if (a.name.toLowerCase().substr(0,2)=='on')  {
25292                 node.removeAttribute(a.name);
25293                 continue;
25294             }
25295             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25296                 node.removeAttribute(a.name);
25297                 continue;
25298             }
25299             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25300                 cleanAttr(a.name,a.value); // fixme..
25301                 continue;
25302             }
25303             if (a.name == 'style') {
25304                 cleanStyle(a.name,a.value);
25305                 continue;
25306             }
25307             /// clean up MS crap..
25308             // tecnically this should be a list of valid class'es..
25309             
25310             
25311             if (a.name == 'class') {
25312                 if (a.value.match(/^Mso/)) {
25313                     node.removeAttribute('class');
25314                 }
25315                 
25316                 if (a.value.match(/^body$/)) {
25317                     node.removeAttribute('class');
25318                 }
25319                 continue;
25320             }
25321             
25322             // style cleanup!?
25323             // class cleanup?
25324             
25325         }
25326         
25327         
25328         this.cleanUpChildren(node);
25329         
25330         
25331     },
25332     
25333     /**
25334      * Clean up MS wordisms...
25335      */
25336     cleanWord : function(node)
25337     {
25338         if (!node) {
25339             this.cleanWord(this.doc.body);
25340             return;
25341         }
25342         
25343         if(
25344                 node.nodeName == 'SPAN' &&
25345                 !node.hasAttributes() &&
25346                 node.childNodes.length == 1 &&
25347                 node.firstChild.nodeName == "#text"  
25348         ) {
25349             var textNode = node.firstChild;
25350             node.removeChild(textNode);
25351             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25352                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25353             }
25354             node.parentNode.insertBefore(textNode, node);
25355             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25356                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25357             }
25358             node.parentNode.removeChild(node);
25359         }
25360         
25361         if (node.nodeName == "#text") {
25362             // clean up silly Windows -- stuff?
25363             return; 
25364         }
25365         if (node.nodeName == "#comment") {
25366             node.parentNode.removeChild(node);
25367             // clean up silly Windows -- stuff?
25368             return; 
25369         }
25370         
25371         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25372             node.parentNode.removeChild(node);
25373             return;
25374         }
25375         //Roo.log(node.tagName);
25376         // remove - but keep children..
25377         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25378             //Roo.log('-- removed');
25379             while (node.childNodes.length) {
25380                 var cn = node.childNodes[0];
25381                 node.removeChild(cn);
25382                 node.parentNode.insertBefore(cn, node);
25383                 // move node to parent - and clean it..
25384                 this.cleanWord(cn);
25385             }
25386             node.parentNode.removeChild(node);
25387             /// no need to iterate chidlren = it's got none..
25388             //this.iterateChildren(node, this.cleanWord);
25389             return;
25390         }
25391         // clean styles
25392         if (node.className.length) {
25393             
25394             var cn = node.className.split(/\W+/);
25395             var cna = [];
25396             Roo.each(cn, function(cls) {
25397                 if (cls.match(/Mso[a-zA-Z]+/)) {
25398                     return;
25399                 }
25400                 cna.push(cls);
25401             });
25402             node.className = cna.length ? cna.join(' ') : '';
25403             if (!cna.length) {
25404                 node.removeAttribute("class");
25405             }
25406         }
25407         
25408         if (node.hasAttribute("lang")) {
25409             node.removeAttribute("lang");
25410         }
25411         
25412         if (node.hasAttribute("style")) {
25413             
25414             var styles = node.getAttribute("style").split(";");
25415             var nstyle = [];
25416             Roo.each(styles, function(s) {
25417                 if (!s.match(/:/)) {
25418                     return;
25419                 }
25420                 var kv = s.split(":");
25421                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25422                     return;
25423                 }
25424                 // what ever is left... we allow.
25425                 nstyle.push(s);
25426             });
25427             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25428             if (!nstyle.length) {
25429                 node.removeAttribute('style');
25430             }
25431         }
25432         this.iterateChildren(node, this.cleanWord);
25433         
25434         
25435         
25436     },
25437     /**
25438      * iterateChildren of a Node, calling fn each time, using this as the scole..
25439      * @param {DomNode} node node to iterate children of.
25440      * @param {Function} fn method of this class to call on each item.
25441      */
25442     iterateChildren : function(node, fn)
25443     {
25444         if (!node.childNodes.length) {
25445                 return;
25446         }
25447         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25448            fn.call(this, node.childNodes[i])
25449         }
25450     },
25451     
25452     
25453     /**
25454      * cleanTableWidths.
25455      *
25456      * Quite often pasting from word etc.. results in tables with column and widths.
25457      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25458      *
25459      */
25460     cleanTableWidths : function(node)
25461     {
25462          
25463          
25464         if (!node) {
25465             this.cleanTableWidths(this.doc.body);
25466             return;
25467         }
25468         
25469         // ignore list...
25470         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25471             return; 
25472         }
25473         Roo.log(node.tagName);
25474         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25475             this.iterateChildren(node, this.cleanTableWidths);
25476             return;
25477         }
25478         if (node.hasAttribute('width')) {
25479             node.removeAttribute('width');
25480         }
25481         
25482          
25483         if (node.hasAttribute("style")) {
25484             // pretty basic...
25485             
25486             var styles = node.getAttribute("style").split(";");
25487             var nstyle = [];
25488             Roo.each(styles, function(s) {
25489                 if (!s.match(/:/)) {
25490                     return;
25491                 }
25492                 var kv = s.split(":");
25493                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25494                     return;
25495                 }
25496                 // what ever is left... we allow.
25497                 nstyle.push(s);
25498             });
25499             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25500             if (!nstyle.length) {
25501                 node.removeAttribute('style');
25502             }
25503         }
25504         
25505         this.iterateChildren(node, this.cleanTableWidths);
25506         
25507         
25508     },
25509     
25510     
25511     
25512     
25513     domToHTML : function(currentElement, depth, nopadtext) {
25514         
25515         depth = depth || 0;
25516         nopadtext = nopadtext || false;
25517     
25518         if (!currentElement) {
25519             return this.domToHTML(this.doc.body);
25520         }
25521         
25522         //Roo.log(currentElement);
25523         var j;
25524         var allText = false;
25525         var nodeName = currentElement.nodeName;
25526         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25527         
25528         if  (nodeName == '#text') {
25529             
25530             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25531         }
25532         
25533         
25534         var ret = '';
25535         if (nodeName != 'BODY') {
25536              
25537             var i = 0;
25538             // Prints the node tagName, such as <A>, <IMG>, etc
25539             if (tagName) {
25540                 var attr = [];
25541                 for(i = 0; i < currentElement.attributes.length;i++) {
25542                     // quoting?
25543                     var aname = currentElement.attributes.item(i).name;
25544                     if (!currentElement.attributes.item(i).value.length) {
25545                         continue;
25546                     }
25547                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25548                 }
25549                 
25550                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25551             } 
25552             else {
25553                 
25554                 // eack
25555             }
25556         } else {
25557             tagName = false;
25558         }
25559         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25560             return ret;
25561         }
25562         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25563             nopadtext = true;
25564         }
25565         
25566         
25567         // Traverse the tree
25568         i = 0;
25569         var currentElementChild = currentElement.childNodes.item(i);
25570         var allText = true;
25571         var innerHTML  = '';
25572         lastnode = '';
25573         while (currentElementChild) {
25574             // Formatting code (indent the tree so it looks nice on the screen)
25575             var nopad = nopadtext;
25576             if (lastnode == 'SPAN') {
25577                 nopad  = true;
25578             }
25579             // text
25580             if  (currentElementChild.nodeName == '#text') {
25581                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25582                 toadd = nopadtext ? toadd : toadd.trim();
25583                 if (!nopad && toadd.length > 80) {
25584                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25585                 }
25586                 innerHTML  += toadd;
25587                 
25588                 i++;
25589                 currentElementChild = currentElement.childNodes.item(i);
25590                 lastNode = '';
25591                 continue;
25592             }
25593             allText = false;
25594             
25595             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25596                 
25597             // Recursively traverse the tree structure of the child node
25598             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25599             lastnode = currentElementChild.nodeName;
25600             i++;
25601             currentElementChild=currentElement.childNodes.item(i);
25602         }
25603         
25604         ret += innerHTML;
25605         
25606         if (!allText) {
25607                 // The remaining code is mostly for formatting the tree
25608             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25609         }
25610         
25611         
25612         if (tagName) {
25613             ret+= "</"+tagName+">";
25614         }
25615         return ret;
25616         
25617     },
25618         
25619     applyBlacklists : function()
25620     {
25621         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25622         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25623         
25624         this.white = [];
25625         this.black = [];
25626         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25627             if (b.indexOf(tag) > -1) {
25628                 return;
25629             }
25630             this.white.push(tag);
25631             
25632         }, this);
25633         
25634         Roo.each(w, function(tag) {
25635             if (b.indexOf(tag) > -1) {
25636                 return;
25637             }
25638             if (this.white.indexOf(tag) > -1) {
25639                 return;
25640             }
25641             this.white.push(tag);
25642             
25643         }, this);
25644         
25645         
25646         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25647             if (w.indexOf(tag) > -1) {
25648                 return;
25649             }
25650             this.black.push(tag);
25651             
25652         }, this);
25653         
25654         Roo.each(b, function(tag) {
25655             if (w.indexOf(tag) > -1) {
25656                 return;
25657             }
25658             if (this.black.indexOf(tag) > -1) {
25659                 return;
25660             }
25661             this.black.push(tag);
25662             
25663         }, this);
25664         
25665         
25666         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25667         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25668         
25669         this.cwhite = [];
25670         this.cblack = [];
25671         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25672             if (b.indexOf(tag) > -1) {
25673                 return;
25674             }
25675             this.cwhite.push(tag);
25676             
25677         }, this);
25678         
25679         Roo.each(w, function(tag) {
25680             if (b.indexOf(tag) > -1) {
25681                 return;
25682             }
25683             if (this.cwhite.indexOf(tag) > -1) {
25684                 return;
25685             }
25686             this.cwhite.push(tag);
25687             
25688         }, this);
25689         
25690         
25691         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25692             if (w.indexOf(tag) > -1) {
25693                 return;
25694             }
25695             this.cblack.push(tag);
25696             
25697         }, this);
25698         
25699         Roo.each(b, function(tag) {
25700             if (w.indexOf(tag) > -1) {
25701                 return;
25702             }
25703             if (this.cblack.indexOf(tag) > -1) {
25704                 return;
25705             }
25706             this.cblack.push(tag);
25707             
25708         }, this);
25709     },
25710     
25711     setStylesheets : function(stylesheets)
25712     {
25713         if(typeof(stylesheets) == 'string'){
25714             Roo.get(this.iframe.contentDocument.head).createChild({
25715                 tag : 'link',
25716                 rel : 'stylesheet',
25717                 type : 'text/css',
25718                 href : stylesheets
25719             });
25720             
25721             return;
25722         }
25723         var _this = this;
25724      
25725         Roo.each(stylesheets, function(s) {
25726             if(!s.length){
25727                 return;
25728             }
25729             
25730             Roo.get(_this.iframe.contentDocument.head).createChild({
25731                 tag : 'link',
25732                 rel : 'stylesheet',
25733                 type : 'text/css',
25734                 href : s
25735             });
25736         });
25737
25738         
25739     },
25740     
25741     removeStylesheets : function()
25742     {
25743         var _this = this;
25744         
25745         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25746             s.remove();
25747         });
25748     },
25749     
25750     setStyle : function(style)
25751     {
25752         Roo.get(this.iframe.contentDocument.head).createChild({
25753             tag : 'style',
25754             type : 'text/css',
25755             html : style
25756         });
25757
25758         return;
25759     }
25760     
25761     // hide stuff that is not compatible
25762     /**
25763      * @event blur
25764      * @hide
25765      */
25766     /**
25767      * @event change
25768      * @hide
25769      */
25770     /**
25771      * @event focus
25772      * @hide
25773      */
25774     /**
25775      * @event specialkey
25776      * @hide
25777      */
25778     /**
25779      * @cfg {String} fieldClass @hide
25780      */
25781     /**
25782      * @cfg {String} focusClass @hide
25783      */
25784     /**
25785      * @cfg {String} autoCreate @hide
25786      */
25787     /**
25788      * @cfg {String} inputType @hide
25789      */
25790     /**
25791      * @cfg {String} invalidClass @hide
25792      */
25793     /**
25794      * @cfg {String} invalidText @hide
25795      */
25796     /**
25797      * @cfg {String} msgFx @hide
25798      */
25799     /**
25800      * @cfg {String} validateOnBlur @hide
25801      */
25802 });
25803
25804 Roo.HtmlEditorCore.white = [
25805         'area', 'br', 'img', 'input', 'hr', 'wbr',
25806         
25807        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25808        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25809        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25810        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25811        'table',   'ul',         'xmp', 
25812        
25813        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25814       'thead',   'tr', 
25815      
25816       'dir', 'menu', 'ol', 'ul', 'dl',
25817        
25818       'embed',  'object'
25819 ];
25820
25821
25822 Roo.HtmlEditorCore.black = [
25823     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25824         'applet', // 
25825         'base',   'basefont', 'bgsound', 'blink',  'body', 
25826         'frame',  'frameset', 'head',    'html',   'ilayer', 
25827         'iframe', 'layer',  'link',     'meta',    'object',   
25828         'script', 'style' ,'title',  'xml' // clean later..
25829 ];
25830 Roo.HtmlEditorCore.clean = [
25831     'script', 'style', 'title', 'xml'
25832 ];
25833 Roo.HtmlEditorCore.remove = [
25834     'font'
25835 ];
25836 // attributes..
25837
25838 Roo.HtmlEditorCore.ablack = [
25839     'on'
25840 ];
25841     
25842 Roo.HtmlEditorCore.aclean = [ 
25843     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25844 ];
25845
25846 // protocols..
25847 Roo.HtmlEditorCore.pwhite= [
25848         'http',  'https',  'mailto'
25849 ];
25850
25851 // white listed style attributes.
25852 Roo.HtmlEditorCore.cwhite= [
25853       //  'text-align', /// default is to allow most things..
25854       
25855          
25856 //        'font-size'//??
25857 ];
25858
25859 // black listed style attributes.
25860 Roo.HtmlEditorCore.cblack= [
25861       //  'font-size' -- this can be set by the project 
25862 ];
25863
25864
25865 Roo.HtmlEditorCore.swapCodes   =[ 
25866     [    8211, "&#8211;" ], 
25867     [    8212, "&#8212;" ], 
25868     [    8216,  "'" ],  
25869     [    8217, "'" ],  
25870     [    8220, '"' ],  
25871     [    8221, '"' ],  
25872     [    8226, "*" ],  
25873     [    8230, "..." ]
25874 ]; 
25875
25876     /*
25877  * - LGPL
25878  *
25879  * HtmlEditor
25880  * 
25881  */
25882
25883 /**
25884  * @class Roo.bootstrap.HtmlEditor
25885  * @extends Roo.bootstrap.TextArea
25886  * Bootstrap HtmlEditor class
25887
25888  * @constructor
25889  * Create a new HtmlEditor
25890  * @param {Object} config The config object
25891  */
25892
25893 Roo.bootstrap.HtmlEditor = function(config){
25894     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25895     if (!this.toolbars) {
25896         this.toolbars = [];
25897     }
25898     
25899     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25900     this.addEvents({
25901             /**
25902              * @event initialize
25903              * Fires when the editor is fully initialized (including the iframe)
25904              * @param {HtmlEditor} this
25905              */
25906             initialize: true,
25907             /**
25908              * @event activate
25909              * Fires when the editor is first receives the focus. Any insertion must wait
25910              * until after this event.
25911              * @param {HtmlEditor} this
25912              */
25913             activate: true,
25914              /**
25915              * @event beforesync
25916              * Fires before the textarea is updated with content from the editor iframe. Return false
25917              * to cancel the sync.
25918              * @param {HtmlEditor} this
25919              * @param {String} html
25920              */
25921             beforesync: true,
25922              /**
25923              * @event beforepush
25924              * Fires before the iframe editor is updated with content from the textarea. Return false
25925              * to cancel the push.
25926              * @param {HtmlEditor} this
25927              * @param {String} html
25928              */
25929             beforepush: true,
25930              /**
25931              * @event sync
25932              * Fires when the textarea is updated with content from the editor iframe.
25933              * @param {HtmlEditor} this
25934              * @param {String} html
25935              */
25936             sync: true,
25937              /**
25938              * @event push
25939              * Fires when the iframe editor is updated with content from the textarea.
25940              * @param {HtmlEditor} this
25941              * @param {String} html
25942              */
25943             push: true,
25944              /**
25945              * @event editmodechange
25946              * Fires when the editor switches edit modes
25947              * @param {HtmlEditor} this
25948              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25949              */
25950             editmodechange: true,
25951             /**
25952              * @event editorevent
25953              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25954              * @param {HtmlEditor} this
25955              */
25956             editorevent: true,
25957             /**
25958              * @event firstfocus
25959              * Fires when on first focus - needed by toolbars..
25960              * @param {HtmlEditor} this
25961              */
25962             firstfocus: true,
25963             /**
25964              * @event autosave
25965              * Auto save the htmlEditor value as a file into Events
25966              * @param {HtmlEditor} this
25967              */
25968             autosave: true,
25969             /**
25970              * @event savedpreview
25971              * preview the saved version of htmlEditor
25972              * @param {HtmlEditor} this
25973              */
25974             savedpreview: true
25975         });
25976 };
25977
25978
25979 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25980     
25981     
25982       /**
25983      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25984      */
25985     toolbars : false,
25986     
25987      /**
25988     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25989     */
25990     btns : [],
25991    
25992      /**
25993      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25994      *                        Roo.resizable.
25995      */
25996     resizable : false,
25997      /**
25998      * @cfg {Number} height (in pixels)
25999      */   
26000     height: 300,
26001    /**
26002      * @cfg {Number} width (in pixels)
26003      */   
26004     width: false,
26005     
26006     /**
26007      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26008      * 
26009      */
26010     stylesheets: false,
26011     
26012     // id of frame..
26013     frameId: false,
26014     
26015     // private properties
26016     validationEvent : false,
26017     deferHeight: true,
26018     initialized : false,
26019     activated : false,
26020     
26021     onFocus : Roo.emptyFn,
26022     iframePad:3,
26023     hideMode:'offsets',
26024     
26025     tbContainer : false,
26026     
26027     bodyCls : '',
26028     
26029     toolbarContainer :function() {
26030         return this.wrap.select('.x-html-editor-tb',true).first();
26031     },
26032
26033     /**
26034      * Protected method that will not generally be called directly. It
26035      * is called when the editor creates its toolbar. Override this method if you need to
26036      * add custom toolbar buttons.
26037      * @param {HtmlEditor} editor
26038      */
26039     createToolbar : function(){
26040         Roo.log('renewing');
26041         Roo.log("create toolbars");
26042         
26043         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26044         this.toolbars[0].render(this.toolbarContainer());
26045         
26046         return;
26047         
26048 //        if (!editor.toolbars || !editor.toolbars.length) {
26049 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26050 //        }
26051 //        
26052 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26053 //            editor.toolbars[i] = Roo.factory(
26054 //                    typeof(editor.toolbars[i]) == 'string' ?
26055 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26056 //                Roo.bootstrap.HtmlEditor);
26057 //            editor.toolbars[i].init(editor);
26058 //        }
26059     },
26060
26061      
26062     // private
26063     onRender : function(ct, position)
26064     {
26065        // Roo.log("Call onRender: " + this.xtype);
26066         var _t = this;
26067         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26068       
26069         this.wrap = this.inputEl().wrap({
26070             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26071         });
26072         
26073         this.editorcore.onRender(ct, position);
26074          
26075         if (this.resizable) {
26076             this.resizeEl = new Roo.Resizable(this.wrap, {
26077                 pinned : true,
26078                 wrap: true,
26079                 dynamic : true,
26080                 minHeight : this.height,
26081                 height: this.height,
26082                 handles : this.resizable,
26083                 width: this.width,
26084                 listeners : {
26085                     resize : function(r, w, h) {
26086                         _t.onResize(w,h); // -something
26087                     }
26088                 }
26089             });
26090             
26091         }
26092         this.createToolbar(this);
26093        
26094         
26095         if(!this.width && this.resizable){
26096             this.setSize(this.wrap.getSize());
26097         }
26098         if (this.resizeEl) {
26099             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26100             // should trigger onReize..
26101         }
26102         
26103     },
26104
26105     // private
26106     onResize : function(w, h)
26107     {
26108         Roo.log('resize: ' +w + ',' + h );
26109         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26110         var ew = false;
26111         var eh = false;
26112         
26113         if(this.inputEl() ){
26114             if(typeof w == 'number'){
26115                 var aw = w - this.wrap.getFrameWidth('lr');
26116                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26117                 ew = aw;
26118             }
26119             if(typeof h == 'number'){
26120                  var tbh = -11;  // fixme it needs to tool bar size!
26121                 for (var i =0; i < this.toolbars.length;i++) {
26122                     // fixme - ask toolbars for heights?
26123                     tbh += this.toolbars[i].el.getHeight();
26124                     //if (this.toolbars[i].footer) {
26125                     //    tbh += this.toolbars[i].footer.el.getHeight();
26126                     //}
26127                 }
26128               
26129                 
26130                 
26131                 
26132                 
26133                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26134                 ah -= 5; // knock a few pixes off for look..
26135                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26136                 var eh = ah;
26137             }
26138         }
26139         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26140         this.editorcore.onResize(ew,eh);
26141         
26142     },
26143
26144     /**
26145      * Toggles the editor between standard and source edit mode.
26146      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26147      */
26148     toggleSourceEdit : function(sourceEditMode)
26149     {
26150         this.editorcore.toggleSourceEdit(sourceEditMode);
26151         
26152         if(this.editorcore.sourceEditMode){
26153             Roo.log('editor - showing textarea');
26154             
26155 //            Roo.log('in');
26156 //            Roo.log(this.syncValue());
26157             this.syncValue();
26158             this.inputEl().removeClass(['hide', 'x-hidden']);
26159             this.inputEl().dom.removeAttribute('tabIndex');
26160             this.inputEl().focus();
26161         }else{
26162             Roo.log('editor - hiding textarea');
26163 //            Roo.log('out')
26164 //            Roo.log(this.pushValue()); 
26165             this.pushValue();
26166             
26167             this.inputEl().addClass(['hide', 'x-hidden']);
26168             this.inputEl().dom.setAttribute('tabIndex', -1);
26169             //this.deferFocus();
26170         }
26171          
26172         if(this.resizable){
26173             this.setSize(this.wrap.getSize());
26174         }
26175         
26176         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26177     },
26178  
26179     // private (for BoxComponent)
26180     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26181
26182     // private (for BoxComponent)
26183     getResizeEl : function(){
26184         return this.wrap;
26185     },
26186
26187     // private (for BoxComponent)
26188     getPositionEl : function(){
26189         return this.wrap;
26190     },
26191
26192     // private
26193     initEvents : function(){
26194         this.originalValue = this.getValue();
26195     },
26196
26197 //    /**
26198 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26199 //     * @method
26200 //     */
26201 //    markInvalid : Roo.emptyFn,
26202 //    /**
26203 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26204 //     * @method
26205 //     */
26206 //    clearInvalid : Roo.emptyFn,
26207
26208     setValue : function(v){
26209         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26210         this.editorcore.pushValue();
26211     },
26212
26213      
26214     // private
26215     deferFocus : function(){
26216         this.focus.defer(10, this);
26217     },
26218
26219     // doc'ed in Field
26220     focus : function(){
26221         this.editorcore.focus();
26222         
26223     },
26224       
26225
26226     // private
26227     onDestroy : function(){
26228         
26229         
26230         
26231         if(this.rendered){
26232             
26233             for (var i =0; i < this.toolbars.length;i++) {
26234                 // fixme - ask toolbars for heights?
26235                 this.toolbars[i].onDestroy();
26236             }
26237             
26238             this.wrap.dom.innerHTML = '';
26239             this.wrap.remove();
26240         }
26241     },
26242
26243     // private
26244     onFirstFocus : function(){
26245         //Roo.log("onFirstFocus");
26246         this.editorcore.onFirstFocus();
26247          for (var i =0; i < this.toolbars.length;i++) {
26248             this.toolbars[i].onFirstFocus();
26249         }
26250         
26251     },
26252     
26253     // private
26254     syncValue : function()
26255     {   
26256         this.editorcore.syncValue();
26257     },
26258     
26259     pushValue : function()
26260     {   
26261         this.editorcore.pushValue();
26262     }
26263      
26264     
26265     // hide stuff that is not compatible
26266     /**
26267      * @event blur
26268      * @hide
26269      */
26270     /**
26271      * @event change
26272      * @hide
26273      */
26274     /**
26275      * @event focus
26276      * @hide
26277      */
26278     /**
26279      * @event specialkey
26280      * @hide
26281      */
26282     /**
26283      * @cfg {String} fieldClass @hide
26284      */
26285     /**
26286      * @cfg {String} focusClass @hide
26287      */
26288     /**
26289      * @cfg {String} autoCreate @hide
26290      */
26291     /**
26292      * @cfg {String} inputType @hide
26293      */
26294      
26295     /**
26296      * @cfg {String} invalidText @hide
26297      */
26298     /**
26299      * @cfg {String} msgFx @hide
26300      */
26301     /**
26302      * @cfg {String} validateOnBlur @hide
26303      */
26304 });
26305  
26306     
26307    
26308    
26309    
26310       
26311 Roo.namespace('Roo.bootstrap.htmleditor');
26312 /**
26313  * @class Roo.bootstrap.HtmlEditorToolbar1
26314  * Basic Toolbar
26315  * 
26316  * @example
26317  * Usage:
26318  *
26319  new Roo.bootstrap.HtmlEditor({
26320     ....
26321     toolbars : [
26322         new Roo.bootstrap.HtmlEditorToolbar1({
26323             disable : { fonts: 1 , format: 1, ..., ... , ...],
26324             btns : [ .... ]
26325         })
26326     }
26327      
26328  * 
26329  * @cfg {Object} disable List of elements to disable..
26330  * @cfg {Array} btns List of additional buttons.
26331  * 
26332  * 
26333  * NEEDS Extra CSS? 
26334  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26335  */
26336  
26337 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26338 {
26339     
26340     Roo.apply(this, config);
26341     
26342     // default disabled, based on 'good practice'..
26343     this.disable = this.disable || {};
26344     Roo.applyIf(this.disable, {
26345         fontSize : true,
26346         colors : true,
26347         specialElements : true
26348     });
26349     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26350     
26351     this.editor = config.editor;
26352     this.editorcore = config.editor.editorcore;
26353     
26354     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26355     
26356     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26357     // dont call parent... till later.
26358 }
26359 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26360      
26361     bar : true,
26362     
26363     editor : false,
26364     editorcore : false,
26365     
26366     
26367     formats : [
26368         "p" ,  
26369         "h1","h2","h3","h4","h5","h6", 
26370         "pre", "code", 
26371         "abbr", "acronym", "address", "cite", "samp", "var",
26372         'div','span'
26373     ],
26374     
26375     onRender : function(ct, position)
26376     {
26377        // Roo.log("Call onRender: " + this.xtype);
26378         
26379        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26380        Roo.log(this.el);
26381        this.el.dom.style.marginBottom = '0';
26382        var _this = this;
26383        var editorcore = this.editorcore;
26384        var editor= this.editor;
26385        
26386        var children = [];
26387        var btn = function(id,cmd , toggle, handler, html){
26388        
26389             var  event = toggle ? 'toggle' : 'click';
26390        
26391             var a = {
26392                 size : 'sm',
26393                 xtype: 'Button',
26394                 xns: Roo.bootstrap,
26395                 //glyphicon : id,
26396                 fa: id,
26397                 cmd : id || cmd,
26398                 enableToggle:toggle !== false,
26399                 html : html || '',
26400                 pressed : toggle ? false : null,
26401                 listeners : {}
26402             };
26403             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26404                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26405             };
26406             children.push(a);
26407             return a;
26408        }
26409        
26410     //    var cb_box = function...
26411         
26412         var style = {
26413                 xtype: 'Button',
26414                 size : 'sm',
26415                 xns: Roo.bootstrap,
26416                 fa : 'font',
26417                 //html : 'submit'
26418                 menu : {
26419                     xtype: 'Menu',
26420                     xns: Roo.bootstrap,
26421                     items:  []
26422                 }
26423         };
26424         Roo.each(this.formats, function(f) {
26425             style.menu.items.push({
26426                 xtype :'MenuItem',
26427                 xns: Roo.bootstrap,
26428                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26429                 tagname : f,
26430                 listeners : {
26431                     click : function()
26432                     {
26433                         editorcore.insertTag(this.tagname);
26434                         editor.focus();
26435                     }
26436                 }
26437                 
26438             });
26439         });
26440         children.push(style);   
26441         
26442         btn('bold',false,true);
26443         btn('italic',false,true);
26444         btn('align-left', 'justifyleft',true);
26445         btn('align-center', 'justifycenter',true);
26446         btn('align-right' , 'justifyright',true);
26447         btn('link', false, false, function(btn) {
26448             //Roo.log("create link?");
26449             var url = prompt(this.createLinkText, this.defaultLinkValue);
26450             if(url && url != 'http:/'+'/'){
26451                 this.editorcore.relayCmd('createlink', url);
26452             }
26453         }),
26454         btn('list','insertunorderedlist',true);
26455         btn('pencil', false,true, function(btn){
26456                 Roo.log(this);
26457                 this.toggleSourceEdit(btn.pressed);
26458         });
26459         
26460         if (this.editor.btns.length > 0) {
26461             for (var i = 0; i<this.editor.btns.length; i++) {
26462                 children.push(this.editor.btns[i]);
26463             }
26464         }
26465         
26466         /*
26467         var cog = {
26468                 xtype: 'Button',
26469                 size : 'sm',
26470                 xns: Roo.bootstrap,
26471                 glyphicon : 'cog',
26472                 //html : 'submit'
26473                 menu : {
26474                     xtype: 'Menu',
26475                     xns: Roo.bootstrap,
26476                     items:  []
26477                 }
26478         };
26479         
26480         cog.menu.items.push({
26481             xtype :'MenuItem',
26482             xns: Roo.bootstrap,
26483             html : Clean styles,
26484             tagname : f,
26485             listeners : {
26486                 click : function()
26487                 {
26488                     editorcore.insertTag(this.tagname);
26489                     editor.focus();
26490                 }
26491             }
26492             
26493         });
26494        */
26495         
26496          
26497        this.xtype = 'NavSimplebar';
26498         
26499         for(var i=0;i< children.length;i++) {
26500             
26501             this.buttons.add(this.addxtypeChild(children[i]));
26502             
26503         }
26504         
26505         editor.on('editorevent', this.updateToolbar, this);
26506     },
26507     onBtnClick : function(id)
26508     {
26509        this.editorcore.relayCmd(id);
26510        this.editorcore.focus();
26511     },
26512     
26513     /**
26514      * Protected method that will not generally be called directly. It triggers
26515      * a toolbar update by reading the markup state of the current selection in the editor.
26516      */
26517     updateToolbar: function(){
26518
26519         if(!this.editorcore.activated){
26520             this.editor.onFirstFocus(); // is this neeed?
26521             return;
26522         }
26523
26524         var btns = this.buttons; 
26525         var doc = this.editorcore.doc;
26526         btns.get('bold').setActive(doc.queryCommandState('bold'));
26527         btns.get('italic').setActive(doc.queryCommandState('italic'));
26528         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26529         
26530         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26531         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26532         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26533         
26534         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26535         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26536          /*
26537         
26538         var ans = this.editorcore.getAllAncestors();
26539         if (this.formatCombo) {
26540             
26541             
26542             var store = this.formatCombo.store;
26543             this.formatCombo.setValue("");
26544             for (var i =0; i < ans.length;i++) {
26545                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26546                     // select it..
26547                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26548                     break;
26549                 }
26550             }
26551         }
26552         
26553         
26554         
26555         // hides menus... - so this cant be on a menu...
26556         Roo.bootstrap.MenuMgr.hideAll();
26557         */
26558         Roo.bootstrap.MenuMgr.hideAll();
26559         //this.editorsyncValue();
26560     },
26561     onFirstFocus: function() {
26562         this.buttons.each(function(item){
26563            item.enable();
26564         });
26565     },
26566     toggleSourceEdit : function(sourceEditMode){
26567         
26568           
26569         if(sourceEditMode){
26570             Roo.log("disabling buttons");
26571            this.buttons.each( function(item){
26572                 if(item.cmd != 'pencil'){
26573                     item.disable();
26574                 }
26575             });
26576           
26577         }else{
26578             Roo.log("enabling buttons");
26579             if(this.editorcore.initialized){
26580                 this.buttons.each( function(item){
26581                     item.enable();
26582                 });
26583             }
26584             
26585         }
26586         Roo.log("calling toggole on editor");
26587         // tell the editor that it's been pressed..
26588         this.editor.toggleSourceEdit(sourceEditMode);
26589        
26590     }
26591 });
26592
26593
26594
26595
26596  
26597 /*
26598  * - LGPL
26599  */
26600
26601 /**
26602  * @class Roo.bootstrap.Markdown
26603  * @extends Roo.bootstrap.TextArea
26604  * Bootstrap Showdown editable area
26605  * @cfg {string} content
26606  * 
26607  * @constructor
26608  * Create a new Showdown
26609  */
26610
26611 Roo.bootstrap.Markdown = function(config){
26612     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26613    
26614 };
26615
26616 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26617     
26618     editing :false,
26619     
26620     initEvents : function()
26621     {
26622         
26623         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26624         this.markdownEl = this.el.createChild({
26625             cls : 'roo-markdown-area'
26626         });
26627         this.inputEl().addClass('d-none');
26628         if (this.getValue() == '') {
26629             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26630             
26631         } else {
26632             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26633         }
26634         this.markdownEl.on('click', this.toggleTextEdit, this);
26635         this.on('blur', this.toggleTextEdit, this);
26636         this.on('specialkey', this.resizeTextArea, this);
26637     },
26638     
26639     toggleTextEdit : function()
26640     {
26641         var sh = this.markdownEl.getHeight();
26642         this.inputEl().addClass('d-none');
26643         this.markdownEl.addClass('d-none');
26644         if (!this.editing) {
26645             // show editor?
26646             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26647             this.inputEl().removeClass('d-none');
26648             this.inputEl().focus();
26649             this.editing = true;
26650             return;
26651         }
26652         // show showdown...
26653         this.updateMarkdown();
26654         this.markdownEl.removeClass('d-none');
26655         this.editing = false;
26656         return;
26657     },
26658     updateMarkdown : function()
26659     {
26660         if (this.getValue() == '') {
26661             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26662             return;
26663         }
26664  
26665         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26666     },
26667     
26668     resizeTextArea: function () {
26669         
26670         var sh = 100;
26671         Roo.log([sh, this.getValue().split("\n").length * 30]);
26672         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26673     },
26674     setValue : function(val)
26675     {
26676         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26677         if (!this.editing) {
26678             this.updateMarkdown();
26679         }
26680         
26681     },
26682     focus : function()
26683     {
26684         if (!this.editing) {
26685             this.toggleTextEdit();
26686         }
26687         
26688     }
26689
26690
26691 });
26692 /**
26693  * @class Roo.bootstrap.Table.AbstractSelectionModel
26694  * @extends Roo.util.Observable
26695  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26696  * implemented by descendant classes.  This class should not be directly instantiated.
26697  * @constructor
26698  */
26699 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26700     this.locked = false;
26701     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26702 };
26703
26704
26705 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26706     /** @ignore Called by the grid automatically. Do not call directly. */
26707     init : function(grid){
26708         this.grid = grid;
26709         this.initEvents();
26710     },
26711
26712     /**
26713      * Locks the selections.
26714      */
26715     lock : function(){
26716         this.locked = true;
26717     },
26718
26719     /**
26720      * Unlocks the selections.
26721      */
26722     unlock : function(){
26723         this.locked = false;
26724     },
26725
26726     /**
26727      * Returns true if the selections are locked.
26728      * @return {Boolean}
26729      */
26730     isLocked : function(){
26731         return this.locked;
26732     },
26733     
26734     
26735     initEvents : function ()
26736     {
26737         
26738     }
26739 });
26740 /**
26741  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26742  * @class Roo.bootstrap.Table.RowSelectionModel
26743  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26744  * It supports multiple selections and keyboard selection/navigation. 
26745  * @constructor
26746  * @param {Object} config
26747  */
26748
26749 Roo.bootstrap.Table.RowSelectionModel = function(config){
26750     Roo.apply(this, config);
26751     this.selections = new Roo.util.MixedCollection(false, function(o){
26752         return o.id;
26753     });
26754
26755     this.last = false;
26756     this.lastActive = false;
26757
26758     this.addEvents({
26759         /**
26760              * @event selectionchange
26761              * Fires when the selection changes
26762              * @param {SelectionModel} this
26763              */
26764             "selectionchange" : true,
26765         /**
26766              * @event afterselectionchange
26767              * Fires after the selection changes (eg. by key press or clicking)
26768              * @param {SelectionModel} this
26769              */
26770             "afterselectionchange" : true,
26771         /**
26772              * @event beforerowselect
26773              * Fires when a row is selected being selected, return false to cancel.
26774              * @param {SelectionModel} this
26775              * @param {Number} rowIndex The selected index
26776              * @param {Boolean} keepExisting False if other selections will be cleared
26777              */
26778             "beforerowselect" : true,
26779         /**
26780              * @event rowselect
26781              * Fires when a row is selected.
26782              * @param {SelectionModel} this
26783              * @param {Number} rowIndex The selected index
26784              * @param {Roo.data.Record} r The record
26785              */
26786             "rowselect" : true,
26787         /**
26788              * @event rowdeselect
26789              * Fires when a row is deselected.
26790              * @param {SelectionModel} this
26791              * @param {Number} rowIndex The selected index
26792              */
26793         "rowdeselect" : true
26794     });
26795     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26796     this.locked = false;
26797  };
26798
26799 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26800     /**
26801      * @cfg {Boolean} singleSelect
26802      * True to allow selection of only one row at a time (defaults to false)
26803      */
26804     singleSelect : false,
26805
26806     // private
26807     initEvents : function()
26808     {
26809
26810         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26811         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26812         //}else{ // allow click to work like normal
26813          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26814         //}
26815         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26816         this.grid.on("rowclick", this.handleMouseDown, this);
26817         
26818         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26819             "up" : function(e){
26820                 if(!e.shiftKey){
26821                     this.selectPrevious(e.shiftKey);
26822                 }else if(this.last !== false && this.lastActive !== false){
26823                     var last = this.last;
26824                     this.selectRange(this.last,  this.lastActive-1);
26825                     this.grid.getView().focusRow(this.lastActive);
26826                     if(last !== false){
26827                         this.last = last;
26828                     }
26829                 }else{
26830                     this.selectFirstRow();
26831                 }
26832                 this.fireEvent("afterselectionchange", this);
26833             },
26834             "down" : function(e){
26835                 if(!e.shiftKey){
26836                     this.selectNext(e.shiftKey);
26837                 }else if(this.last !== false && this.lastActive !== false){
26838                     var last = this.last;
26839                     this.selectRange(this.last,  this.lastActive+1);
26840                     this.grid.getView().focusRow(this.lastActive);
26841                     if(last !== false){
26842                         this.last = last;
26843                     }
26844                 }else{
26845                     this.selectFirstRow();
26846                 }
26847                 this.fireEvent("afterselectionchange", this);
26848             },
26849             scope: this
26850         });
26851         this.grid.store.on('load', function(){
26852             this.selections.clear();
26853         },this);
26854         /*
26855         var view = this.grid.view;
26856         view.on("refresh", this.onRefresh, this);
26857         view.on("rowupdated", this.onRowUpdated, this);
26858         view.on("rowremoved", this.onRemove, this);
26859         */
26860     },
26861
26862     // private
26863     onRefresh : function()
26864     {
26865         var ds = this.grid.store, i, v = this.grid.view;
26866         var s = this.selections;
26867         s.each(function(r){
26868             if((i = ds.indexOfId(r.id)) != -1){
26869                 v.onRowSelect(i);
26870             }else{
26871                 s.remove(r);
26872             }
26873         });
26874     },
26875
26876     // private
26877     onRemove : function(v, index, r){
26878         this.selections.remove(r);
26879     },
26880
26881     // private
26882     onRowUpdated : function(v, index, r){
26883         if(this.isSelected(r)){
26884             v.onRowSelect(index);
26885         }
26886     },
26887
26888     /**
26889      * Select records.
26890      * @param {Array} records The records to select
26891      * @param {Boolean} keepExisting (optional) True to keep existing selections
26892      */
26893     selectRecords : function(records, keepExisting)
26894     {
26895         if(!keepExisting){
26896             this.clearSelections();
26897         }
26898             var ds = this.grid.store;
26899         for(var i = 0, len = records.length; i < len; i++){
26900             this.selectRow(ds.indexOf(records[i]), true);
26901         }
26902     },
26903
26904     /**
26905      * Gets the number of selected rows.
26906      * @return {Number}
26907      */
26908     getCount : function(){
26909         return this.selections.length;
26910     },
26911
26912     /**
26913      * Selects the first row in the grid.
26914      */
26915     selectFirstRow : function(){
26916         this.selectRow(0);
26917     },
26918
26919     /**
26920      * Select the last row.
26921      * @param {Boolean} keepExisting (optional) True to keep existing selections
26922      */
26923     selectLastRow : function(keepExisting){
26924         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26925         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26926     },
26927
26928     /**
26929      * Selects the row immediately following the last selected row.
26930      * @param {Boolean} keepExisting (optional) True to keep existing selections
26931      */
26932     selectNext : function(keepExisting)
26933     {
26934             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26935             this.selectRow(this.last+1, keepExisting);
26936             this.grid.getView().focusRow(this.last);
26937         }
26938     },
26939
26940     /**
26941      * Selects the row that precedes the last selected row.
26942      * @param {Boolean} keepExisting (optional) True to keep existing selections
26943      */
26944     selectPrevious : function(keepExisting){
26945         if(this.last){
26946             this.selectRow(this.last-1, keepExisting);
26947             this.grid.getView().focusRow(this.last);
26948         }
26949     },
26950
26951     /**
26952      * Returns the selected records
26953      * @return {Array} Array of selected records
26954      */
26955     getSelections : function(){
26956         return [].concat(this.selections.items);
26957     },
26958
26959     /**
26960      * Returns the first selected record.
26961      * @return {Record}
26962      */
26963     getSelected : function(){
26964         return this.selections.itemAt(0);
26965     },
26966
26967
26968     /**
26969      * Clears all selections.
26970      */
26971     clearSelections : function(fast)
26972     {
26973         if(this.locked) {
26974             return;
26975         }
26976         if(fast !== true){
26977                 var ds = this.grid.store;
26978             var s = this.selections;
26979             s.each(function(r){
26980                 this.deselectRow(ds.indexOfId(r.id));
26981             }, this);
26982             s.clear();
26983         }else{
26984             this.selections.clear();
26985         }
26986         this.last = false;
26987     },
26988
26989
26990     /**
26991      * Selects all rows.
26992      */
26993     selectAll : function(){
26994         if(this.locked) {
26995             return;
26996         }
26997         this.selections.clear();
26998         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26999             this.selectRow(i, true);
27000         }
27001     },
27002
27003     /**
27004      * Returns True if there is a selection.
27005      * @return {Boolean}
27006      */
27007     hasSelection : function(){
27008         return this.selections.length > 0;
27009     },
27010
27011     /**
27012      * Returns True if the specified row is selected.
27013      * @param {Number/Record} record The record or index of the record to check
27014      * @return {Boolean}
27015      */
27016     isSelected : function(index){
27017             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27018         return (r && this.selections.key(r.id) ? true : false);
27019     },
27020
27021     /**
27022      * Returns True if the specified record id is selected.
27023      * @param {String} id The id of record to check
27024      * @return {Boolean}
27025      */
27026     isIdSelected : function(id){
27027         return (this.selections.key(id) ? true : false);
27028     },
27029
27030
27031     // private
27032     handleMouseDBClick : function(e, t){
27033         
27034     },
27035     // private
27036     handleMouseDown : function(e, t)
27037     {
27038             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27039         if(this.isLocked() || rowIndex < 0 ){
27040             return;
27041         };
27042         if(e.shiftKey && this.last !== false){
27043             var last = this.last;
27044             this.selectRange(last, rowIndex, e.ctrlKey);
27045             this.last = last; // reset the last
27046             t.focus();
27047     
27048         }else{
27049             var isSelected = this.isSelected(rowIndex);
27050             //Roo.log("select row:" + rowIndex);
27051             if(isSelected){
27052                 this.deselectRow(rowIndex);
27053             } else {
27054                         this.selectRow(rowIndex, true);
27055             }
27056     
27057             /*
27058                 if(e.button !== 0 && isSelected){
27059                 alert('rowIndex 2: ' + rowIndex);
27060                     view.focusRow(rowIndex);
27061                 }else if(e.ctrlKey && isSelected){
27062                     this.deselectRow(rowIndex);
27063                 }else if(!isSelected){
27064                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27065                     view.focusRow(rowIndex);
27066                 }
27067             */
27068         }
27069         this.fireEvent("afterselectionchange", this);
27070     },
27071     // private
27072     handleDragableRowClick :  function(grid, rowIndex, e) 
27073     {
27074         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27075             this.selectRow(rowIndex, false);
27076             grid.view.focusRow(rowIndex);
27077              this.fireEvent("afterselectionchange", this);
27078         }
27079     },
27080     
27081     /**
27082      * Selects multiple rows.
27083      * @param {Array} rows Array of the indexes of the row to select
27084      * @param {Boolean} keepExisting (optional) True to keep existing selections
27085      */
27086     selectRows : function(rows, keepExisting){
27087         if(!keepExisting){
27088             this.clearSelections();
27089         }
27090         for(var i = 0, len = rows.length; i < len; i++){
27091             this.selectRow(rows[i], true);
27092         }
27093     },
27094
27095     /**
27096      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27097      * @param {Number} startRow The index of the first row in the range
27098      * @param {Number} endRow The index of the last row in the range
27099      * @param {Boolean} keepExisting (optional) True to retain existing selections
27100      */
27101     selectRange : function(startRow, endRow, keepExisting){
27102         if(this.locked) {
27103             return;
27104         }
27105         if(!keepExisting){
27106             this.clearSelections();
27107         }
27108         if(startRow <= endRow){
27109             for(var i = startRow; i <= endRow; i++){
27110                 this.selectRow(i, true);
27111             }
27112         }else{
27113             for(var i = startRow; i >= endRow; i--){
27114                 this.selectRow(i, true);
27115             }
27116         }
27117     },
27118
27119     /**
27120      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27121      * @param {Number} startRow The index of the first row in the range
27122      * @param {Number} endRow The index of the last row in the range
27123      */
27124     deselectRange : function(startRow, endRow, preventViewNotify){
27125         if(this.locked) {
27126             return;
27127         }
27128         for(var i = startRow; i <= endRow; i++){
27129             this.deselectRow(i, preventViewNotify);
27130         }
27131     },
27132
27133     /**
27134      * Selects a row.
27135      * @param {Number} row The index of the row to select
27136      * @param {Boolean} keepExisting (optional) True to keep existing selections
27137      */
27138     selectRow : function(index, keepExisting, preventViewNotify)
27139     {
27140             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27141             return;
27142         }
27143         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27144             if(!keepExisting || this.singleSelect){
27145                 this.clearSelections();
27146             }
27147             
27148             var r = this.grid.store.getAt(index);
27149             //console.log('selectRow - record id :' + r.id);
27150             
27151             this.selections.add(r);
27152             this.last = this.lastActive = index;
27153             if(!preventViewNotify){
27154                 var proxy = new Roo.Element(
27155                                 this.grid.getRowDom(index)
27156                 );
27157                 proxy.addClass('bg-info info');
27158             }
27159             this.fireEvent("rowselect", this, index, r);
27160             this.fireEvent("selectionchange", this);
27161         }
27162     },
27163
27164     /**
27165      * Deselects a row.
27166      * @param {Number} row The index of the row to deselect
27167      */
27168     deselectRow : function(index, preventViewNotify)
27169     {
27170         if(this.locked) {
27171             return;
27172         }
27173         if(this.last == index){
27174             this.last = false;
27175         }
27176         if(this.lastActive == index){
27177             this.lastActive = false;
27178         }
27179         
27180         var r = this.grid.store.getAt(index);
27181         if (!r) {
27182             return;
27183         }
27184         
27185         this.selections.remove(r);
27186         //.console.log('deselectRow - record id :' + r.id);
27187         if(!preventViewNotify){
27188         
27189             var proxy = new Roo.Element(
27190                 this.grid.getRowDom(index)
27191             );
27192             proxy.removeClass('bg-info info');
27193         }
27194         this.fireEvent("rowdeselect", this, index);
27195         this.fireEvent("selectionchange", this);
27196     },
27197
27198     // private
27199     restoreLast : function(){
27200         if(this._last){
27201             this.last = this._last;
27202         }
27203     },
27204
27205     // private
27206     acceptsNav : function(row, col, cm){
27207         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27208     },
27209
27210     // private
27211     onEditorKey : function(field, e){
27212         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27213         if(k == e.TAB){
27214             e.stopEvent();
27215             ed.completeEdit();
27216             if(e.shiftKey){
27217                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27218             }else{
27219                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27220             }
27221         }else if(k == e.ENTER && !e.ctrlKey){
27222             e.stopEvent();
27223             ed.completeEdit();
27224             if(e.shiftKey){
27225                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27226             }else{
27227                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27228             }
27229         }else if(k == e.ESC){
27230             ed.cancelEdit();
27231         }
27232         if(newCell){
27233             g.startEditing(newCell[0], newCell[1]);
27234         }
27235     }
27236 });
27237 /*
27238  * Based on:
27239  * Ext JS Library 1.1.1
27240  * Copyright(c) 2006-2007, Ext JS, LLC.
27241  *
27242  * Originally Released Under LGPL - original licence link has changed is not relivant.
27243  *
27244  * Fork - LGPL
27245  * <script type="text/javascript">
27246  */
27247  
27248 /**
27249  * @class Roo.bootstrap.PagingToolbar
27250  * @extends Roo.bootstrap.NavSimplebar
27251  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27252  * @constructor
27253  * Create a new PagingToolbar
27254  * @param {Object} config The config object
27255  * @param {Roo.data.Store} store
27256  */
27257 Roo.bootstrap.PagingToolbar = function(config)
27258 {
27259     // old args format still supported... - xtype is prefered..
27260         // created from xtype...
27261     
27262     this.ds = config.dataSource;
27263     
27264     if (config.store && !this.ds) {
27265         this.store= Roo.factory(config.store, Roo.data);
27266         this.ds = this.store;
27267         this.ds.xmodule = this.xmodule || false;
27268     }
27269     
27270     this.toolbarItems = [];
27271     if (config.items) {
27272         this.toolbarItems = config.items;
27273     }
27274     
27275     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27276     
27277     this.cursor = 0;
27278     
27279     if (this.ds) { 
27280         this.bind(this.ds);
27281     }
27282     
27283     if (Roo.bootstrap.version == 4) {
27284         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27285     } else {
27286         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27287     }
27288     
27289 };
27290
27291 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27292     /**
27293      * @cfg {Roo.data.Store} dataSource
27294      * The underlying data store providing the paged data
27295      */
27296     /**
27297      * @cfg {String/HTMLElement/Element} container
27298      * container The id or element that will contain the toolbar
27299      */
27300     /**
27301      * @cfg {Boolean} displayInfo
27302      * True to display the displayMsg (defaults to false)
27303      */
27304     /**
27305      * @cfg {Number} pageSize
27306      * The number of records to display per page (defaults to 20)
27307      */
27308     pageSize: 20,
27309     /**
27310      * @cfg {String} displayMsg
27311      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27312      */
27313     displayMsg : 'Displaying {0} - {1} of {2}',
27314     /**
27315      * @cfg {String} emptyMsg
27316      * The message to display when no records are found (defaults to "No data to display")
27317      */
27318     emptyMsg : 'No data to display',
27319     /**
27320      * Customizable piece of the default paging text (defaults to "Page")
27321      * @type String
27322      */
27323     beforePageText : "Page",
27324     /**
27325      * Customizable piece of the default paging text (defaults to "of %0")
27326      * @type String
27327      */
27328     afterPageText : "of {0}",
27329     /**
27330      * Customizable piece of the default paging text (defaults to "First Page")
27331      * @type String
27332      */
27333     firstText : "First Page",
27334     /**
27335      * Customizable piece of the default paging text (defaults to "Previous Page")
27336      * @type String
27337      */
27338     prevText : "Previous Page",
27339     /**
27340      * Customizable piece of the default paging text (defaults to "Next Page")
27341      * @type String
27342      */
27343     nextText : "Next Page",
27344     /**
27345      * Customizable piece of the default paging text (defaults to "Last Page")
27346      * @type String
27347      */
27348     lastText : "Last Page",
27349     /**
27350      * Customizable piece of the default paging text (defaults to "Refresh")
27351      * @type String
27352      */
27353     refreshText : "Refresh",
27354
27355     buttons : false,
27356     // private
27357     onRender : function(ct, position) 
27358     {
27359         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27360         this.navgroup.parentId = this.id;
27361         this.navgroup.onRender(this.el, null);
27362         // add the buttons to the navgroup
27363         
27364         if(this.displayInfo){
27365             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27366             this.displayEl = this.el.select('.x-paging-info', true).first();
27367 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27368 //            this.displayEl = navel.el.select('span',true).first();
27369         }
27370         
27371         var _this = this;
27372         
27373         if(this.buttons){
27374             Roo.each(_this.buttons, function(e){ // this might need to use render????
27375                Roo.factory(e).render(_this.el);
27376             });
27377         }
27378             
27379         Roo.each(_this.toolbarItems, function(e) {
27380             _this.navgroup.addItem(e);
27381         });
27382         
27383         
27384         this.first = this.navgroup.addItem({
27385             tooltip: this.firstText,
27386             cls: "prev btn-outline-secondary",
27387             html : ' <i class="fa fa-step-backward"></i>',
27388             disabled: true,
27389             preventDefault: true,
27390             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27391         });
27392         
27393         this.prev =  this.navgroup.addItem({
27394             tooltip: this.prevText,
27395             cls: "prev btn-outline-secondary",
27396             html : ' <i class="fa fa-backward"></i>',
27397             disabled: true,
27398             preventDefault: true,
27399             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27400         });
27401     //this.addSeparator();
27402         
27403         
27404         var field = this.navgroup.addItem( {
27405             tagtype : 'span',
27406             cls : 'x-paging-position  btn-outline-secondary',
27407              disabled: true,
27408             html : this.beforePageText  +
27409                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27410                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27411          } ); //?? escaped?
27412         
27413         this.field = field.el.select('input', true).first();
27414         this.field.on("keydown", this.onPagingKeydown, this);
27415         this.field.on("focus", function(){this.dom.select();});
27416     
27417     
27418         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27419         //this.field.setHeight(18);
27420         //this.addSeparator();
27421         this.next = this.navgroup.addItem({
27422             tooltip: this.nextText,
27423             cls: "next btn-outline-secondary",
27424             html : ' <i class="fa fa-forward"></i>',
27425             disabled: true,
27426             preventDefault: true,
27427             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27428         });
27429         this.last = this.navgroup.addItem({
27430             tooltip: this.lastText,
27431             html : ' <i class="fa fa-step-forward"></i>',
27432             cls: "next btn-outline-secondary",
27433             disabled: true,
27434             preventDefault: true,
27435             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27436         });
27437     //this.addSeparator();
27438         this.loading = this.navgroup.addItem({
27439             tooltip: this.refreshText,
27440             cls: "btn-outline-secondary",
27441             html : ' <i class="fa fa-refresh"></i>',
27442             preventDefault: true,
27443             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27444         });
27445         
27446     },
27447
27448     // private
27449     updateInfo : function(){
27450         if(this.displayEl){
27451             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27452             var msg = count == 0 ?
27453                 this.emptyMsg :
27454                 String.format(
27455                     this.displayMsg,
27456                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27457                 );
27458             this.displayEl.update(msg);
27459         }
27460     },
27461
27462     // private
27463     onLoad : function(ds, r, o)
27464     {
27465         this.cursor = o.params && o.params.start ? o.params.start : 0;
27466         
27467         var d = this.getPageData(),
27468             ap = d.activePage,
27469             ps = d.pages;
27470         
27471         
27472         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27473         this.field.dom.value = ap;
27474         this.first.setDisabled(ap == 1);
27475         this.prev.setDisabled(ap == 1);
27476         this.next.setDisabled(ap == ps);
27477         this.last.setDisabled(ap == ps);
27478         this.loading.enable();
27479         this.updateInfo();
27480     },
27481
27482     // private
27483     getPageData : function(){
27484         var total = this.ds.getTotalCount();
27485         return {
27486             total : total,
27487             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27488             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27489         };
27490     },
27491
27492     // private
27493     onLoadError : function(){
27494         this.loading.enable();
27495     },
27496
27497     // private
27498     onPagingKeydown : function(e){
27499         var k = e.getKey();
27500         var d = this.getPageData();
27501         if(k == e.RETURN){
27502             var v = this.field.dom.value, pageNum;
27503             if(!v || isNaN(pageNum = parseInt(v, 10))){
27504                 this.field.dom.value = d.activePage;
27505                 return;
27506             }
27507             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27508             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27509             e.stopEvent();
27510         }
27511         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))
27512         {
27513           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27514           this.field.dom.value = pageNum;
27515           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27516           e.stopEvent();
27517         }
27518         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27519         {
27520           var v = this.field.dom.value, pageNum; 
27521           var increment = (e.shiftKey) ? 10 : 1;
27522           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27523                 increment *= -1;
27524           }
27525           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27526             this.field.dom.value = d.activePage;
27527             return;
27528           }
27529           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27530           {
27531             this.field.dom.value = parseInt(v, 10) + increment;
27532             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27533             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27534           }
27535           e.stopEvent();
27536         }
27537     },
27538
27539     // private
27540     beforeLoad : function(){
27541         if(this.loading){
27542             this.loading.disable();
27543         }
27544     },
27545
27546     // private
27547     onClick : function(which){
27548         
27549         var ds = this.ds;
27550         if (!ds) {
27551             return;
27552         }
27553         
27554         switch(which){
27555             case "first":
27556                 ds.load({params:{start: 0, limit: this.pageSize}});
27557             break;
27558             case "prev":
27559                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27560             break;
27561             case "next":
27562                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27563             break;
27564             case "last":
27565                 var total = ds.getTotalCount();
27566                 var extra = total % this.pageSize;
27567                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27568                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27569             break;
27570             case "refresh":
27571                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27572             break;
27573         }
27574     },
27575
27576     /**
27577      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27578      * @param {Roo.data.Store} store The data store to unbind
27579      */
27580     unbind : function(ds){
27581         ds.un("beforeload", this.beforeLoad, this);
27582         ds.un("load", this.onLoad, this);
27583         ds.un("loadexception", this.onLoadError, this);
27584         ds.un("remove", this.updateInfo, this);
27585         ds.un("add", this.updateInfo, this);
27586         this.ds = undefined;
27587     },
27588
27589     /**
27590      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27591      * @param {Roo.data.Store} store The data store to bind
27592      */
27593     bind : function(ds){
27594         ds.on("beforeload", this.beforeLoad, this);
27595         ds.on("load", this.onLoad, this);
27596         ds.on("loadexception", this.onLoadError, this);
27597         ds.on("remove", this.updateInfo, this);
27598         ds.on("add", this.updateInfo, this);
27599         this.ds = ds;
27600     }
27601 });/*
27602  * - LGPL
27603  *
27604  * element
27605  * 
27606  */
27607
27608 /**
27609  * @class Roo.bootstrap.MessageBar
27610  * @extends Roo.bootstrap.Component
27611  * Bootstrap MessageBar class
27612  * @cfg {String} html contents of the MessageBar
27613  * @cfg {String} weight (info | success | warning | danger) default info
27614  * @cfg {String} beforeClass insert the bar before the given class
27615  * @cfg {Boolean} closable (true | false) default false
27616  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27617  * 
27618  * @constructor
27619  * Create a new Element
27620  * @param {Object} config The config object
27621  */
27622
27623 Roo.bootstrap.MessageBar = function(config){
27624     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27625 };
27626
27627 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27628     
27629     html: '',
27630     weight: 'info',
27631     closable: false,
27632     fixed: false,
27633     beforeClass: 'bootstrap-sticky-wrap',
27634     
27635     getAutoCreate : function(){
27636         
27637         var cfg = {
27638             tag: 'div',
27639             cls: 'alert alert-dismissable alert-' + this.weight,
27640             cn: [
27641                 {
27642                     tag: 'span',
27643                     cls: 'message',
27644                     html: this.html || ''
27645                 }
27646             ]
27647         };
27648         
27649         if(this.fixed){
27650             cfg.cls += ' alert-messages-fixed';
27651         }
27652         
27653         if(this.closable){
27654             cfg.cn.push({
27655                 tag: 'button',
27656                 cls: 'close',
27657                 html: 'x'
27658             });
27659         }
27660         
27661         return cfg;
27662     },
27663     
27664     onRender : function(ct, position)
27665     {
27666         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27667         
27668         if(!this.el){
27669             var cfg = Roo.apply({},  this.getAutoCreate());
27670             cfg.id = Roo.id();
27671             
27672             if (this.cls) {
27673                 cfg.cls += ' ' + this.cls;
27674             }
27675             if (this.style) {
27676                 cfg.style = this.style;
27677             }
27678             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27679             
27680             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27681         }
27682         
27683         this.el.select('>button.close').on('click', this.hide, this);
27684         
27685     },
27686     
27687     show : function()
27688     {
27689         if (!this.rendered) {
27690             this.render();
27691         }
27692         
27693         this.el.show();
27694         
27695         this.fireEvent('show', this);
27696         
27697     },
27698     
27699     hide : function()
27700     {
27701         if (!this.rendered) {
27702             this.render();
27703         }
27704         
27705         this.el.hide();
27706         
27707         this.fireEvent('hide', this);
27708     },
27709     
27710     update : function()
27711     {
27712 //        var e = this.el.dom.firstChild;
27713 //        
27714 //        if(this.closable){
27715 //            e = e.nextSibling;
27716 //        }
27717 //        
27718 //        e.data = this.html || '';
27719
27720         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27721     }
27722    
27723 });
27724
27725  
27726
27727      /*
27728  * - LGPL
27729  *
27730  * Graph
27731  * 
27732  */
27733
27734
27735 /**
27736  * @class Roo.bootstrap.Graph
27737  * @extends Roo.bootstrap.Component
27738  * Bootstrap Graph class
27739 > Prameters
27740  -sm {number} sm 4
27741  -md {number} md 5
27742  @cfg {String} graphtype  bar | vbar | pie
27743  @cfg {number} g_x coodinator | centre x (pie)
27744  @cfg {number} g_y coodinator | centre y (pie)
27745  @cfg {number} g_r radius (pie)
27746  @cfg {number} g_height height of the chart (respected by all elements in the set)
27747  @cfg {number} g_width width of the chart (respected by all elements in the set)
27748  @cfg {Object} title The title of the chart
27749     
27750  -{Array}  values
27751  -opts (object) options for the chart 
27752      o {
27753      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27754      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27755      o vgutter (number)
27756      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.
27757      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27758      o to
27759      o stretch (boolean)
27760      o }
27761  -opts (object) options for the pie
27762      o{
27763      o cut
27764      o startAngle (number)
27765      o endAngle (number)
27766      } 
27767  *
27768  * @constructor
27769  * Create a new Input
27770  * @param {Object} config The config object
27771  */
27772
27773 Roo.bootstrap.Graph = function(config){
27774     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27775     
27776     this.addEvents({
27777         // img events
27778         /**
27779          * @event click
27780          * The img click event for the img.
27781          * @param {Roo.EventObject} e
27782          */
27783         "click" : true
27784     });
27785 };
27786
27787 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27788     
27789     sm: 4,
27790     md: 5,
27791     graphtype: 'bar',
27792     g_height: 250,
27793     g_width: 400,
27794     g_x: 50,
27795     g_y: 50,
27796     g_r: 30,
27797     opts:{
27798         //g_colors: this.colors,
27799         g_type: 'soft',
27800         g_gutter: '20%'
27801
27802     },
27803     title : false,
27804
27805     getAutoCreate : function(){
27806         
27807         var cfg = {
27808             tag: 'div',
27809             html : null
27810         };
27811         
27812         
27813         return  cfg;
27814     },
27815
27816     onRender : function(ct,position){
27817         
27818         
27819         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27820         
27821         if (typeof(Raphael) == 'undefined') {
27822             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27823             return;
27824         }
27825         
27826         this.raphael = Raphael(this.el.dom);
27827         
27828                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27829                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27830                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27831                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27832                 /*
27833                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27834                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27835                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27836                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27837                 
27838                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27839                 r.barchart(330, 10, 300, 220, data1);
27840                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27841                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27842                 */
27843                 
27844                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27845                 // r.barchart(30, 30, 560, 250,  xdata, {
27846                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27847                 //     axis : "0 0 1 1",
27848                 //     axisxlabels :  xdata
27849                 //     //yvalues : cols,
27850                    
27851                 // });
27852 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27853 //        
27854 //        this.load(null,xdata,{
27855 //                axis : "0 0 1 1",
27856 //                axisxlabels :  xdata
27857 //                });
27858
27859     },
27860
27861     load : function(graphtype,xdata,opts)
27862     {
27863         this.raphael.clear();
27864         if(!graphtype) {
27865             graphtype = this.graphtype;
27866         }
27867         if(!opts){
27868             opts = this.opts;
27869         }
27870         var r = this.raphael,
27871             fin = function () {
27872                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27873             },
27874             fout = function () {
27875                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27876             },
27877             pfin = function() {
27878                 this.sector.stop();
27879                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27880
27881                 if (this.label) {
27882                     this.label[0].stop();
27883                     this.label[0].attr({ r: 7.5 });
27884                     this.label[1].attr({ "font-weight": 800 });
27885                 }
27886             },
27887             pfout = function() {
27888                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27889
27890                 if (this.label) {
27891                     this.label[0].animate({ r: 5 }, 500, "bounce");
27892                     this.label[1].attr({ "font-weight": 400 });
27893                 }
27894             };
27895
27896         switch(graphtype){
27897             case 'bar':
27898                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27899                 break;
27900             case 'hbar':
27901                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27902                 break;
27903             case 'pie':
27904 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27905 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27906 //            
27907                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27908                 
27909                 break;
27910
27911         }
27912         
27913         if(this.title){
27914             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27915         }
27916         
27917     },
27918     
27919     setTitle: function(o)
27920     {
27921         this.title = o;
27922     },
27923     
27924     initEvents: function() {
27925         
27926         if(!this.href){
27927             this.el.on('click', this.onClick, this);
27928         }
27929     },
27930     
27931     onClick : function(e)
27932     {
27933         Roo.log('img onclick');
27934         this.fireEvent('click', this, e);
27935     }
27936    
27937 });
27938
27939  
27940 /*
27941  * - LGPL
27942  *
27943  * numberBox
27944  * 
27945  */
27946 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27947
27948 /**
27949  * @class Roo.bootstrap.dash.NumberBox
27950  * @extends Roo.bootstrap.Component
27951  * Bootstrap NumberBox class
27952  * @cfg {String} headline Box headline
27953  * @cfg {String} content Box content
27954  * @cfg {String} icon Box icon
27955  * @cfg {String} footer Footer text
27956  * @cfg {String} fhref Footer href
27957  * 
27958  * @constructor
27959  * Create a new NumberBox
27960  * @param {Object} config The config object
27961  */
27962
27963
27964 Roo.bootstrap.dash.NumberBox = function(config){
27965     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27966     
27967 };
27968
27969 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27970     
27971     headline : '',
27972     content : '',
27973     icon : '',
27974     footer : '',
27975     fhref : '',
27976     ficon : '',
27977     
27978     getAutoCreate : function(){
27979         
27980         var cfg = {
27981             tag : 'div',
27982             cls : 'small-box ',
27983             cn : [
27984                 {
27985                     tag : 'div',
27986                     cls : 'inner',
27987                     cn :[
27988                         {
27989                             tag : 'h3',
27990                             cls : 'roo-headline',
27991                             html : this.headline
27992                         },
27993                         {
27994                             tag : 'p',
27995                             cls : 'roo-content',
27996                             html : this.content
27997                         }
27998                     ]
27999                 }
28000             ]
28001         };
28002         
28003         if(this.icon){
28004             cfg.cn.push({
28005                 tag : 'div',
28006                 cls : 'icon',
28007                 cn :[
28008                     {
28009                         tag : 'i',
28010                         cls : 'ion ' + this.icon
28011                     }
28012                 ]
28013             });
28014         }
28015         
28016         if(this.footer){
28017             var footer = {
28018                 tag : 'a',
28019                 cls : 'small-box-footer',
28020                 href : this.fhref || '#',
28021                 html : this.footer
28022             };
28023             
28024             cfg.cn.push(footer);
28025             
28026         }
28027         
28028         return  cfg;
28029     },
28030
28031     onRender : function(ct,position){
28032         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28033
28034
28035        
28036                 
28037     },
28038
28039     setHeadline: function (value)
28040     {
28041         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28042     },
28043     
28044     setFooter: function (value, href)
28045     {
28046         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28047         
28048         if(href){
28049             this.el.select('a.small-box-footer',true).first().attr('href', href);
28050         }
28051         
28052     },
28053
28054     setContent: function (value)
28055     {
28056         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28057     },
28058
28059     initEvents: function() 
28060     {   
28061         
28062     }
28063     
28064 });
28065
28066  
28067 /*
28068  * - LGPL
28069  *
28070  * TabBox
28071  * 
28072  */
28073 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28074
28075 /**
28076  * @class Roo.bootstrap.dash.TabBox
28077  * @extends Roo.bootstrap.Component
28078  * Bootstrap TabBox class
28079  * @cfg {String} title Title of the TabBox
28080  * @cfg {String} icon Icon of the TabBox
28081  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28082  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28083  * 
28084  * @constructor
28085  * Create a new TabBox
28086  * @param {Object} config The config object
28087  */
28088
28089
28090 Roo.bootstrap.dash.TabBox = function(config){
28091     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28092     this.addEvents({
28093         // raw events
28094         /**
28095          * @event addpane
28096          * When a pane is added
28097          * @param {Roo.bootstrap.dash.TabPane} pane
28098          */
28099         "addpane" : true,
28100         /**
28101          * @event activatepane
28102          * When a pane is activated
28103          * @param {Roo.bootstrap.dash.TabPane} pane
28104          */
28105         "activatepane" : true
28106         
28107          
28108     });
28109     
28110     this.panes = [];
28111 };
28112
28113 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28114
28115     title : '',
28116     icon : false,
28117     showtabs : true,
28118     tabScrollable : false,
28119     
28120     getChildContainer : function()
28121     {
28122         return this.el.select('.tab-content', true).first();
28123     },
28124     
28125     getAutoCreate : function(){
28126         
28127         var header = {
28128             tag: 'li',
28129             cls: 'pull-left header',
28130             html: this.title,
28131             cn : []
28132         };
28133         
28134         if(this.icon){
28135             header.cn.push({
28136                 tag: 'i',
28137                 cls: 'fa ' + this.icon
28138             });
28139         }
28140         
28141         var h = {
28142             tag: 'ul',
28143             cls: 'nav nav-tabs pull-right',
28144             cn: [
28145                 header
28146             ]
28147         };
28148         
28149         if(this.tabScrollable){
28150             h = {
28151                 tag: 'div',
28152                 cls: 'tab-header',
28153                 cn: [
28154                     {
28155                         tag: 'ul',
28156                         cls: 'nav nav-tabs pull-right',
28157                         cn: [
28158                             header
28159                         ]
28160                     }
28161                 ]
28162             };
28163         }
28164         
28165         var cfg = {
28166             tag: 'div',
28167             cls: 'nav-tabs-custom',
28168             cn: [
28169                 h,
28170                 {
28171                     tag: 'div',
28172                     cls: 'tab-content no-padding',
28173                     cn: []
28174                 }
28175             ]
28176         };
28177
28178         return  cfg;
28179     },
28180     initEvents : function()
28181     {
28182         //Roo.log('add add pane handler');
28183         this.on('addpane', this.onAddPane, this);
28184     },
28185      /**
28186      * Updates the box title
28187      * @param {String} html to set the title to.
28188      */
28189     setTitle : function(value)
28190     {
28191         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28192     },
28193     onAddPane : function(pane)
28194     {
28195         this.panes.push(pane);
28196         //Roo.log('addpane');
28197         //Roo.log(pane);
28198         // tabs are rendere left to right..
28199         if(!this.showtabs){
28200             return;
28201         }
28202         
28203         var ctr = this.el.select('.nav-tabs', true).first();
28204          
28205          
28206         var existing = ctr.select('.nav-tab',true);
28207         var qty = existing.getCount();;
28208         
28209         
28210         var tab = ctr.createChild({
28211             tag : 'li',
28212             cls : 'nav-tab' + (qty ? '' : ' active'),
28213             cn : [
28214                 {
28215                     tag : 'a',
28216                     href:'#',
28217                     html : pane.title
28218                 }
28219             ]
28220         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28221         pane.tab = tab;
28222         
28223         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28224         if (!qty) {
28225             pane.el.addClass('active');
28226         }
28227         
28228                 
28229     },
28230     onTabClick : function(ev,un,ob,pane)
28231     {
28232         //Roo.log('tab - prev default');
28233         ev.preventDefault();
28234         
28235         
28236         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28237         pane.tab.addClass('active');
28238         //Roo.log(pane.title);
28239         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28240         // technically we should have a deactivate event.. but maybe add later.
28241         // and it should not de-activate the selected tab...
28242         this.fireEvent('activatepane', pane);
28243         pane.el.addClass('active');
28244         pane.fireEvent('activate');
28245         
28246         
28247     },
28248     
28249     getActivePane : function()
28250     {
28251         var r = false;
28252         Roo.each(this.panes, function(p) {
28253             if(p.el.hasClass('active')){
28254                 r = p;
28255                 return false;
28256             }
28257             
28258             return;
28259         });
28260         
28261         return r;
28262     }
28263     
28264     
28265 });
28266
28267  
28268 /*
28269  * - LGPL
28270  *
28271  * Tab pane
28272  * 
28273  */
28274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28275 /**
28276  * @class Roo.bootstrap.TabPane
28277  * @extends Roo.bootstrap.Component
28278  * Bootstrap TabPane class
28279  * @cfg {Boolean} active (false | true) Default false
28280  * @cfg {String} title title of panel
28281
28282  * 
28283  * @constructor
28284  * Create a new TabPane
28285  * @param {Object} config The config object
28286  */
28287
28288 Roo.bootstrap.dash.TabPane = function(config){
28289     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28290     
28291     this.addEvents({
28292         // raw events
28293         /**
28294          * @event activate
28295          * When a pane is activated
28296          * @param {Roo.bootstrap.dash.TabPane} pane
28297          */
28298         "activate" : true
28299          
28300     });
28301 };
28302
28303 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28304     
28305     active : false,
28306     title : '',
28307     
28308     // the tabBox that this is attached to.
28309     tab : false,
28310      
28311     getAutoCreate : function() 
28312     {
28313         var cfg = {
28314             tag: 'div',
28315             cls: 'tab-pane'
28316         };
28317         
28318         if(this.active){
28319             cfg.cls += ' active';
28320         }
28321         
28322         return cfg;
28323     },
28324     initEvents  : function()
28325     {
28326         //Roo.log('trigger add pane handler');
28327         this.parent().fireEvent('addpane', this)
28328     },
28329     
28330      /**
28331      * Updates the tab title 
28332      * @param {String} html to set the title to.
28333      */
28334     setTitle: function(str)
28335     {
28336         if (!this.tab) {
28337             return;
28338         }
28339         this.title = str;
28340         this.tab.select('a', true).first().dom.innerHTML = str;
28341         
28342     }
28343     
28344     
28345     
28346 });
28347
28348  
28349
28350
28351  /*
28352  * - LGPL
28353  *
28354  * menu
28355  * 
28356  */
28357 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28358
28359 /**
28360  * @class Roo.bootstrap.menu.Menu
28361  * @extends Roo.bootstrap.Component
28362  * Bootstrap Menu class - container for Menu
28363  * @cfg {String} html Text of the menu
28364  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28365  * @cfg {String} icon Font awesome icon
28366  * @cfg {String} pos Menu align to (top | bottom) default bottom
28367  * 
28368  * 
28369  * @constructor
28370  * Create a new Menu
28371  * @param {Object} config The config object
28372  */
28373
28374
28375 Roo.bootstrap.menu.Menu = function(config){
28376     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28377     
28378     this.addEvents({
28379         /**
28380          * @event beforeshow
28381          * Fires before this menu is displayed
28382          * @param {Roo.bootstrap.menu.Menu} this
28383          */
28384         beforeshow : true,
28385         /**
28386          * @event beforehide
28387          * Fires before this menu is hidden
28388          * @param {Roo.bootstrap.menu.Menu} this
28389          */
28390         beforehide : true,
28391         /**
28392          * @event show
28393          * Fires after this menu is displayed
28394          * @param {Roo.bootstrap.menu.Menu} this
28395          */
28396         show : true,
28397         /**
28398          * @event hide
28399          * Fires after this menu is hidden
28400          * @param {Roo.bootstrap.menu.Menu} this
28401          */
28402         hide : true,
28403         /**
28404          * @event click
28405          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28406          * @param {Roo.bootstrap.menu.Menu} this
28407          * @param {Roo.EventObject} e
28408          */
28409         click : true
28410     });
28411     
28412 };
28413
28414 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28415     
28416     submenu : false,
28417     html : '',
28418     weight : 'default',
28419     icon : false,
28420     pos : 'bottom',
28421     
28422     
28423     getChildContainer : function() {
28424         if(this.isSubMenu){
28425             return this.el;
28426         }
28427         
28428         return this.el.select('ul.dropdown-menu', true).first();  
28429     },
28430     
28431     getAutoCreate : function()
28432     {
28433         var text = [
28434             {
28435                 tag : 'span',
28436                 cls : 'roo-menu-text',
28437                 html : this.html
28438             }
28439         ];
28440         
28441         if(this.icon){
28442             text.unshift({
28443                 tag : 'i',
28444                 cls : 'fa ' + this.icon
28445             })
28446         }
28447         
28448         
28449         var cfg = {
28450             tag : 'div',
28451             cls : 'btn-group',
28452             cn : [
28453                 {
28454                     tag : 'button',
28455                     cls : 'dropdown-button btn btn-' + this.weight,
28456                     cn : text
28457                 },
28458                 {
28459                     tag : 'button',
28460                     cls : 'dropdown-toggle btn btn-' + this.weight,
28461                     cn : [
28462                         {
28463                             tag : 'span',
28464                             cls : 'caret'
28465                         }
28466                     ]
28467                 },
28468                 {
28469                     tag : 'ul',
28470                     cls : 'dropdown-menu'
28471                 }
28472             ]
28473             
28474         };
28475         
28476         if(this.pos == 'top'){
28477             cfg.cls += ' dropup';
28478         }
28479         
28480         if(this.isSubMenu){
28481             cfg = {
28482                 tag : 'ul',
28483                 cls : 'dropdown-menu'
28484             }
28485         }
28486         
28487         return cfg;
28488     },
28489     
28490     onRender : function(ct, position)
28491     {
28492         this.isSubMenu = ct.hasClass('dropdown-submenu');
28493         
28494         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28495     },
28496     
28497     initEvents : function() 
28498     {
28499         if(this.isSubMenu){
28500             return;
28501         }
28502         
28503         this.hidden = true;
28504         
28505         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28506         this.triggerEl.on('click', this.onTriggerPress, this);
28507         
28508         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28509         this.buttonEl.on('click', this.onClick, this);
28510         
28511     },
28512     
28513     list : function()
28514     {
28515         if(this.isSubMenu){
28516             return this.el;
28517         }
28518         
28519         return this.el.select('ul.dropdown-menu', true).first();
28520     },
28521     
28522     onClick : function(e)
28523     {
28524         this.fireEvent("click", this, e);
28525     },
28526     
28527     onTriggerPress  : function(e)
28528     {   
28529         if (this.isVisible()) {
28530             this.hide();
28531         } else {
28532             this.show();
28533         }
28534     },
28535     
28536     isVisible : function(){
28537         return !this.hidden;
28538     },
28539     
28540     show : function()
28541     {
28542         this.fireEvent("beforeshow", this);
28543         
28544         this.hidden = false;
28545         this.el.addClass('open');
28546         
28547         Roo.get(document).on("mouseup", this.onMouseUp, this);
28548         
28549         this.fireEvent("show", this);
28550         
28551         
28552     },
28553     
28554     hide : function()
28555     {
28556         this.fireEvent("beforehide", this);
28557         
28558         this.hidden = true;
28559         this.el.removeClass('open');
28560         
28561         Roo.get(document).un("mouseup", this.onMouseUp);
28562         
28563         this.fireEvent("hide", this);
28564     },
28565     
28566     onMouseUp : function()
28567     {
28568         this.hide();
28569     }
28570     
28571 });
28572
28573  
28574  /*
28575  * - LGPL
28576  *
28577  * menu item
28578  * 
28579  */
28580 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28581
28582 /**
28583  * @class Roo.bootstrap.menu.Item
28584  * @extends Roo.bootstrap.Component
28585  * Bootstrap MenuItem class
28586  * @cfg {Boolean} submenu (true | false) default false
28587  * @cfg {String} html text of the item
28588  * @cfg {String} href the link
28589  * @cfg {Boolean} disable (true | false) default false
28590  * @cfg {Boolean} preventDefault (true | false) default true
28591  * @cfg {String} icon Font awesome icon
28592  * @cfg {String} pos Submenu align to (left | right) default right 
28593  * 
28594  * 
28595  * @constructor
28596  * Create a new Item
28597  * @param {Object} config The config object
28598  */
28599
28600
28601 Roo.bootstrap.menu.Item = function(config){
28602     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28603     this.addEvents({
28604         /**
28605          * @event mouseover
28606          * Fires when the mouse is hovering over this menu
28607          * @param {Roo.bootstrap.menu.Item} this
28608          * @param {Roo.EventObject} e
28609          */
28610         mouseover : true,
28611         /**
28612          * @event mouseout
28613          * Fires when the mouse exits this menu
28614          * @param {Roo.bootstrap.menu.Item} this
28615          * @param {Roo.EventObject} e
28616          */
28617         mouseout : true,
28618         // raw events
28619         /**
28620          * @event click
28621          * The raw click event for the entire grid.
28622          * @param {Roo.EventObject} e
28623          */
28624         click : true
28625     });
28626 };
28627
28628 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28629     
28630     submenu : false,
28631     href : '',
28632     html : '',
28633     preventDefault: true,
28634     disable : false,
28635     icon : false,
28636     pos : 'right',
28637     
28638     getAutoCreate : function()
28639     {
28640         var text = [
28641             {
28642                 tag : 'span',
28643                 cls : 'roo-menu-item-text',
28644                 html : this.html
28645             }
28646         ];
28647         
28648         if(this.icon){
28649             text.unshift({
28650                 tag : 'i',
28651                 cls : 'fa ' + this.icon
28652             })
28653         }
28654         
28655         var cfg = {
28656             tag : 'li',
28657             cn : [
28658                 {
28659                     tag : 'a',
28660                     href : this.href || '#',
28661                     cn : text
28662                 }
28663             ]
28664         };
28665         
28666         if(this.disable){
28667             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28668         }
28669         
28670         if(this.submenu){
28671             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28672             
28673             if(this.pos == 'left'){
28674                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28675             }
28676         }
28677         
28678         return cfg;
28679     },
28680     
28681     initEvents : function() 
28682     {
28683         this.el.on('mouseover', this.onMouseOver, this);
28684         this.el.on('mouseout', this.onMouseOut, this);
28685         
28686         this.el.select('a', true).first().on('click', this.onClick, this);
28687         
28688     },
28689     
28690     onClick : function(e)
28691     {
28692         if(this.preventDefault){
28693             e.preventDefault();
28694         }
28695         
28696         this.fireEvent("click", this, e);
28697     },
28698     
28699     onMouseOver : function(e)
28700     {
28701         if(this.submenu && this.pos == 'left'){
28702             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28703         }
28704         
28705         this.fireEvent("mouseover", this, e);
28706     },
28707     
28708     onMouseOut : function(e)
28709     {
28710         this.fireEvent("mouseout", this, e);
28711     }
28712 });
28713
28714  
28715
28716  /*
28717  * - LGPL
28718  *
28719  * menu separator
28720  * 
28721  */
28722 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28723
28724 /**
28725  * @class Roo.bootstrap.menu.Separator
28726  * @extends Roo.bootstrap.Component
28727  * Bootstrap Separator class
28728  * 
28729  * @constructor
28730  * Create a new Separator
28731  * @param {Object} config The config object
28732  */
28733
28734
28735 Roo.bootstrap.menu.Separator = function(config){
28736     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28737 };
28738
28739 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28740     
28741     getAutoCreate : function(){
28742         var cfg = {
28743             tag : 'li',
28744             cls: 'divider'
28745         };
28746         
28747         return cfg;
28748     }
28749    
28750 });
28751
28752  
28753
28754  /*
28755  * - LGPL
28756  *
28757  * Tooltip
28758  * 
28759  */
28760
28761 /**
28762  * @class Roo.bootstrap.Tooltip
28763  * Bootstrap Tooltip class
28764  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28765  * to determine which dom element triggers the tooltip.
28766  * 
28767  * It needs to add support for additional attributes like tooltip-position
28768  * 
28769  * @constructor
28770  * Create a new Toolti
28771  * @param {Object} config The config object
28772  */
28773
28774 Roo.bootstrap.Tooltip = function(config){
28775     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28776     
28777     this.alignment = Roo.bootstrap.Tooltip.alignment;
28778     
28779     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28780         this.alignment = config.alignment;
28781     }
28782     
28783 };
28784
28785 Roo.apply(Roo.bootstrap.Tooltip, {
28786     /**
28787      * @function init initialize tooltip monitoring.
28788      * @static
28789      */
28790     currentEl : false,
28791     currentTip : false,
28792     currentRegion : false,
28793     
28794     //  init : delay?
28795     
28796     init : function()
28797     {
28798         Roo.get(document).on('mouseover', this.enter ,this);
28799         Roo.get(document).on('mouseout', this.leave, this);
28800          
28801         
28802         this.currentTip = new Roo.bootstrap.Tooltip();
28803     },
28804     
28805     enter : function(ev)
28806     {
28807         var dom = ev.getTarget();
28808         
28809         //Roo.log(['enter',dom]);
28810         var el = Roo.fly(dom);
28811         if (this.currentEl) {
28812             //Roo.log(dom);
28813             //Roo.log(this.currentEl);
28814             //Roo.log(this.currentEl.contains(dom));
28815             if (this.currentEl == el) {
28816                 return;
28817             }
28818             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28819                 return;
28820             }
28821
28822         }
28823         
28824         if (this.currentTip.el) {
28825             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28826         }    
28827         //Roo.log(ev);
28828         
28829         if(!el || el.dom == document){
28830             return;
28831         }
28832         
28833         var bindEl = el;
28834         
28835         // you can not look for children, as if el is the body.. then everythign is the child..
28836         if (!el.attr('tooltip')) { //
28837             if (!el.select("[tooltip]").elements.length) {
28838                 return;
28839             }
28840             // is the mouse over this child...?
28841             bindEl = el.select("[tooltip]").first();
28842             var xy = ev.getXY();
28843             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28844                 //Roo.log("not in region.");
28845                 return;
28846             }
28847             //Roo.log("child element over..");
28848             
28849         }
28850         this.currentEl = bindEl;
28851         this.currentTip.bind(bindEl);
28852         this.currentRegion = Roo.lib.Region.getRegion(dom);
28853         this.currentTip.enter();
28854         
28855     },
28856     leave : function(ev)
28857     {
28858         var dom = ev.getTarget();
28859         //Roo.log(['leave',dom]);
28860         if (!this.currentEl) {
28861             return;
28862         }
28863         
28864         
28865         if (dom != this.currentEl.dom) {
28866             return;
28867         }
28868         var xy = ev.getXY();
28869         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28870             return;
28871         }
28872         // only activate leave if mouse cursor is outside... bounding box..
28873         
28874         
28875         
28876         
28877         if (this.currentTip) {
28878             this.currentTip.leave();
28879         }
28880         //Roo.log('clear currentEl');
28881         this.currentEl = false;
28882         
28883         
28884     },
28885     alignment : {
28886         'left' : ['r-l', [-2,0], 'right'],
28887         'right' : ['l-r', [2,0], 'left'],
28888         'bottom' : ['t-b', [0,2], 'top'],
28889         'top' : [ 'b-t', [0,-2], 'bottom']
28890     }
28891     
28892 });
28893
28894
28895 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28896     
28897     
28898     bindEl : false,
28899     
28900     delay : null, // can be { show : 300 , hide: 500}
28901     
28902     timeout : null,
28903     
28904     hoverState : null, //???
28905     
28906     placement : 'bottom', 
28907     
28908     alignment : false,
28909     
28910     getAutoCreate : function(){
28911     
28912         var cfg = {
28913            cls : 'tooltip',   
28914            role : 'tooltip',
28915            cn : [
28916                 {
28917                     cls : 'tooltip-arrow arrow'
28918                 },
28919                 {
28920                     cls : 'tooltip-inner'
28921                 }
28922            ]
28923         };
28924         
28925         return cfg;
28926     },
28927     bind : function(el)
28928     {
28929         this.bindEl = el;
28930     },
28931     
28932     initEvents : function()
28933     {
28934         this.arrowEl = this.el.select('.arrow', true).first();
28935         this.innerEl = this.el.select('.tooltip-inner', true).first();
28936     },
28937     
28938     enter : function () {
28939        
28940         if (this.timeout != null) {
28941             clearTimeout(this.timeout);
28942         }
28943         
28944         this.hoverState = 'in';
28945          //Roo.log("enter - show");
28946         if (!this.delay || !this.delay.show) {
28947             this.show();
28948             return;
28949         }
28950         var _t = this;
28951         this.timeout = setTimeout(function () {
28952             if (_t.hoverState == 'in') {
28953                 _t.show();
28954             }
28955         }, this.delay.show);
28956     },
28957     leave : function()
28958     {
28959         clearTimeout(this.timeout);
28960     
28961         this.hoverState = 'out';
28962          if (!this.delay || !this.delay.hide) {
28963             this.hide();
28964             return;
28965         }
28966        
28967         var _t = this;
28968         this.timeout = setTimeout(function () {
28969             //Roo.log("leave - timeout");
28970             
28971             if (_t.hoverState == 'out') {
28972                 _t.hide();
28973                 Roo.bootstrap.Tooltip.currentEl = false;
28974             }
28975         }, delay);
28976     },
28977     
28978     show : function (msg)
28979     {
28980         if (!this.el) {
28981             this.render(document.body);
28982         }
28983         // set content.
28984         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28985         
28986         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28987         
28988         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28989         
28990         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28991                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28992         
28993         var placement = typeof this.placement == 'function' ?
28994             this.placement.call(this, this.el, on_el) :
28995             this.placement;
28996             
28997         var autoToken = /\s?auto?\s?/i;
28998         var autoPlace = autoToken.test(placement);
28999         if (autoPlace) {
29000             placement = placement.replace(autoToken, '') || 'top';
29001         }
29002         
29003         //this.el.detach()
29004         //this.el.setXY([0,0]);
29005         this.el.show();
29006         //this.el.dom.style.display='block';
29007         
29008         //this.el.appendTo(on_el);
29009         
29010         var p = this.getPosition();
29011         var box = this.el.getBox();
29012         
29013         if (autoPlace) {
29014             // fixme..
29015         }
29016         
29017         var align = this.alignment[placement];
29018         
29019         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29020         
29021         if(placement == 'top' || placement == 'bottom'){
29022             if(xy[0] < 0){
29023                 placement = 'right';
29024             }
29025             
29026             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29027                 placement = 'left';
29028             }
29029             
29030             var scroll = Roo.select('body', true).first().getScroll();
29031             
29032             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29033                 placement = 'top';
29034             }
29035             
29036             align = this.alignment[placement];
29037             
29038             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29039             
29040         }
29041         
29042         this.el.alignTo(this.bindEl, align[0],align[1]);
29043         //var arrow = this.el.select('.arrow',true).first();
29044         //arrow.set(align[2], 
29045         
29046         this.el.addClass(placement);
29047         this.el.addClass("bs-tooltip-"+ placement);
29048         
29049         this.el.addClass('in fade show');
29050         
29051         this.hoverState = null;
29052         
29053         if (this.el.hasClass('fade')) {
29054             // fade it?
29055         }
29056         
29057         
29058         
29059         
29060         
29061     },
29062     hide : function()
29063     {
29064          
29065         if (!this.el) {
29066             return;
29067         }
29068         //this.el.setXY([0,0]);
29069         this.el.removeClass(['show', 'in']);
29070         //this.el.hide();
29071         
29072     }
29073     
29074 });
29075  
29076
29077  /*
29078  * - LGPL
29079  *
29080  * Location Picker
29081  * 
29082  */
29083
29084 /**
29085  * @class Roo.bootstrap.LocationPicker
29086  * @extends Roo.bootstrap.Component
29087  * Bootstrap LocationPicker class
29088  * @cfg {Number} latitude Position when init default 0
29089  * @cfg {Number} longitude Position when init default 0
29090  * @cfg {Number} zoom default 15
29091  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29092  * @cfg {Boolean} mapTypeControl default false
29093  * @cfg {Boolean} disableDoubleClickZoom default false
29094  * @cfg {Boolean} scrollwheel default true
29095  * @cfg {Boolean} streetViewControl default false
29096  * @cfg {Number} radius default 0
29097  * @cfg {String} locationName
29098  * @cfg {Boolean} draggable default true
29099  * @cfg {Boolean} enableAutocomplete default false
29100  * @cfg {Boolean} enableReverseGeocode default true
29101  * @cfg {String} markerTitle
29102  * 
29103  * @constructor
29104  * Create a new LocationPicker
29105  * @param {Object} config The config object
29106  */
29107
29108
29109 Roo.bootstrap.LocationPicker = function(config){
29110     
29111     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29112     
29113     this.addEvents({
29114         /**
29115          * @event initial
29116          * Fires when the picker initialized.
29117          * @param {Roo.bootstrap.LocationPicker} this
29118          * @param {Google Location} location
29119          */
29120         initial : true,
29121         /**
29122          * @event positionchanged
29123          * Fires when the picker position changed.
29124          * @param {Roo.bootstrap.LocationPicker} this
29125          * @param {Google Location} location
29126          */
29127         positionchanged : true,
29128         /**
29129          * @event resize
29130          * Fires when the map resize.
29131          * @param {Roo.bootstrap.LocationPicker} this
29132          */
29133         resize : true,
29134         /**
29135          * @event show
29136          * Fires when the map show.
29137          * @param {Roo.bootstrap.LocationPicker} this
29138          */
29139         show : true,
29140         /**
29141          * @event hide
29142          * Fires when the map hide.
29143          * @param {Roo.bootstrap.LocationPicker} this
29144          */
29145         hide : true,
29146         /**
29147          * @event mapClick
29148          * Fires when click the map.
29149          * @param {Roo.bootstrap.LocationPicker} this
29150          * @param {Map event} e
29151          */
29152         mapClick : true,
29153         /**
29154          * @event mapRightClick
29155          * Fires when right click the map.
29156          * @param {Roo.bootstrap.LocationPicker} this
29157          * @param {Map event} e
29158          */
29159         mapRightClick : true,
29160         /**
29161          * @event markerClick
29162          * Fires when click the marker.
29163          * @param {Roo.bootstrap.LocationPicker} this
29164          * @param {Map event} e
29165          */
29166         markerClick : true,
29167         /**
29168          * @event markerRightClick
29169          * Fires when right click the marker.
29170          * @param {Roo.bootstrap.LocationPicker} this
29171          * @param {Map event} e
29172          */
29173         markerRightClick : true,
29174         /**
29175          * @event OverlayViewDraw
29176          * Fires when OverlayView Draw
29177          * @param {Roo.bootstrap.LocationPicker} this
29178          */
29179         OverlayViewDraw : true,
29180         /**
29181          * @event OverlayViewOnAdd
29182          * Fires when OverlayView Draw
29183          * @param {Roo.bootstrap.LocationPicker} this
29184          */
29185         OverlayViewOnAdd : true,
29186         /**
29187          * @event OverlayViewOnRemove
29188          * Fires when OverlayView Draw
29189          * @param {Roo.bootstrap.LocationPicker} this
29190          */
29191         OverlayViewOnRemove : true,
29192         /**
29193          * @event OverlayViewShow
29194          * Fires when OverlayView Draw
29195          * @param {Roo.bootstrap.LocationPicker} this
29196          * @param {Pixel} cpx
29197          */
29198         OverlayViewShow : true,
29199         /**
29200          * @event OverlayViewHide
29201          * Fires when OverlayView Draw
29202          * @param {Roo.bootstrap.LocationPicker} this
29203          */
29204         OverlayViewHide : true,
29205         /**
29206          * @event loadexception
29207          * Fires when load google lib failed.
29208          * @param {Roo.bootstrap.LocationPicker} this
29209          */
29210         loadexception : true
29211     });
29212         
29213 };
29214
29215 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29216     
29217     gMapContext: false,
29218     
29219     latitude: 0,
29220     longitude: 0,
29221     zoom: 15,
29222     mapTypeId: false,
29223     mapTypeControl: false,
29224     disableDoubleClickZoom: false,
29225     scrollwheel: true,
29226     streetViewControl: false,
29227     radius: 0,
29228     locationName: '',
29229     draggable: true,
29230     enableAutocomplete: false,
29231     enableReverseGeocode: true,
29232     markerTitle: '',
29233     
29234     getAutoCreate: function()
29235     {
29236
29237         var cfg = {
29238             tag: 'div',
29239             cls: 'roo-location-picker'
29240         };
29241         
29242         return cfg
29243     },
29244     
29245     initEvents: function(ct, position)
29246     {       
29247         if(!this.el.getWidth() || this.isApplied()){
29248             return;
29249         }
29250         
29251         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29252         
29253         this.initial();
29254     },
29255     
29256     initial: function()
29257     {
29258         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29259             this.fireEvent('loadexception', this);
29260             return;
29261         }
29262         
29263         if(!this.mapTypeId){
29264             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29265         }
29266         
29267         this.gMapContext = this.GMapContext();
29268         
29269         this.initOverlayView();
29270         
29271         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29272         
29273         var _this = this;
29274                 
29275         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29276             _this.setPosition(_this.gMapContext.marker.position);
29277         });
29278         
29279         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29280             _this.fireEvent('mapClick', this, event);
29281             
29282         });
29283
29284         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29285             _this.fireEvent('mapRightClick', this, event);
29286             
29287         });
29288         
29289         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29290             _this.fireEvent('markerClick', this, event);
29291             
29292         });
29293
29294         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29295             _this.fireEvent('markerRightClick', this, event);
29296             
29297         });
29298         
29299         this.setPosition(this.gMapContext.location);
29300         
29301         this.fireEvent('initial', this, this.gMapContext.location);
29302     },
29303     
29304     initOverlayView: function()
29305     {
29306         var _this = this;
29307         
29308         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29309             
29310             draw: function()
29311             {
29312                 _this.fireEvent('OverlayViewDraw', _this);
29313             },
29314             
29315             onAdd: function()
29316             {
29317                 _this.fireEvent('OverlayViewOnAdd', _this);
29318             },
29319             
29320             onRemove: function()
29321             {
29322                 _this.fireEvent('OverlayViewOnRemove', _this);
29323             },
29324             
29325             show: function(cpx)
29326             {
29327                 _this.fireEvent('OverlayViewShow', _this, cpx);
29328             },
29329             
29330             hide: function()
29331             {
29332                 _this.fireEvent('OverlayViewHide', _this);
29333             }
29334             
29335         });
29336     },
29337     
29338     fromLatLngToContainerPixel: function(event)
29339     {
29340         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29341     },
29342     
29343     isApplied: function() 
29344     {
29345         return this.getGmapContext() == false ? false : true;
29346     },
29347     
29348     getGmapContext: function() 
29349     {
29350         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29351     },
29352     
29353     GMapContext: function() 
29354     {
29355         var position = new google.maps.LatLng(this.latitude, this.longitude);
29356         
29357         var _map = new google.maps.Map(this.el.dom, {
29358             center: position,
29359             zoom: this.zoom,
29360             mapTypeId: this.mapTypeId,
29361             mapTypeControl: this.mapTypeControl,
29362             disableDoubleClickZoom: this.disableDoubleClickZoom,
29363             scrollwheel: this.scrollwheel,
29364             streetViewControl: this.streetViewControl,
29365             locationName: this.locationName,
29366             draggable: this.draggable,
29367             enableAutocomplete: this.enableAutocomplete,
29368             enableReverseGeocode: this.enableReverseGeocode
29369         });
29370         
29371         var _marker = new google.maps.Marker({
29372             position: position,
29373             map: _map,
29374             title: this.markerTitle,
29375             draggable: this.draggable
29376         });
29377         
29378         return {
29379             map: _map,
29380             marker: _marker,
29381             circle: null,
29382             location: position,
29383             radius: this.radius,
29384             locationName: this.locationName,
29385             addressComponents: {
29386                 formatted_address: null,
29387                 addressLine1: null,
29388                 addressLine2: null,
29389                 streetName: null,
29390                 streetNumber: null,
29391                 city: null,
29392                 district: null,
29393                 state: null,
29394                 stateOrProvince: null
29395             },
29396             settings: this,
29397             domContainer: this.el.dom,
29398             geodecoder: new google.maps.Geocoder()
29399         };
29400     },
29401     
29402     drawCircle: function(center, radius, options) 
29403     {
29404         if (this.gMapContext.circle != null) {
29405             this.gMapContext.circle.setMap(null);
29406         }
29407         if (radius > 0) {
29408             radius *= 1;
29409             options = Roo.apply({}, options, {
29410                 strokeColor: "#0000FF",
29411                 strokeOpacity: .35,
29412                 strokeWeight: 2,
29413                 fillColor: "#0000FF",
29414                 fillOpacity: .2
29415             });
29416             
29417             options.map = this.gMapContext.map;
29418             options.radius = radius;
29419             options.center = center;
29420             this.gMapContext.circle = new google.maps.Circle(options);
29421             return this.gMapContext.circle;
29422         }
29423         
29424         return null;
29425     },
29426     
29427     setPosition: function(location) 
29428     {
29429         this.gMapContext.location = location;
29430         this.gMapContext.marker.setPosition(location);
29431         this.gMapContext.map.panTo(location);
29432         this.drawCircle(location, this.gMapContext.radius, {});
29433         
29434         var _this = this;
29435         
29436         if (this.gMapContext.settings.enableReverseGeocode) {
29437             this.gMapContext.geodecoder.geocode({
29438                 latLng: this.gMapContext.location
29439             }, function(results, status) {
29440                 
29441                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29442                     _this.gMapContext.locationName = results[0].formatted_address;
29443                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29444                     
29445                     _this.fireEvent('positionchanged', this, location);
29446                 }
29447             });
29448             
29449             return;
29450         }
29451         
29452         this.fireEvent('positionchanged', this, location);
29453     },
29454     
29455     resize: function()
29456     {
29457         google.maps.event.trigger(this.gMapContext.map, "resize");
29458         
29459         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29460         
29461         this.fireEvent('resize', this);
29462     },
29463     
29464     setPositionByLatLng: function(latitude, longitude)
29465     {
29466         this.setPosition(new google.maps.LatLng(latitude, longitude));
29467     },
29468     
29469     getCurrentPosition: function() 
29470     {
29471         return {
29472             latitude: this.gMapContext.location.lat(),
29473             longitude: this.gMapContext.location.lng()
29474         };
29475     },
29476     
29477     getAddressName: function() 
29478     {
29479         return this.gMapContext.locationName;
29480     },
29481     
29482     getAddressComponents: function() 
29483     {
29484         return this.gMapContext.addressComponents;
29485     },
29486     
29487     address_component_from_google_geocode: function(address_components) 
29488     {
29489         var result = {};
29490         
29491         for (var i = 0; i < address_components.length; i++) {
29492             var component = address_components[i];
29493             if (component.types.indexOf("postal_code") >= 0) {
29494                 result.postalCode = component.short_name;
29495             } else if (component.types.indexOf("street_number") >= 0) {
29496                 result.streetNumber = component.short_name;
29497             } else if (component.types.indexOf("route") >= 0) {
29498                 result.streetName = component.short_name;
29499             } else if (component.types.indexOf("neighborhood") >= 0) {
29500                 result.city = component.short_name;
29501             } else if (component.types.indexOf("locality") >= 0) {
29502                 result.city = component.short_name;
29503             } else if (component.types.indexOf("sublocality") >= 0) {
29504                 result.district = component.short_name;
29505             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29506                 result.stateOrProvince = component.short_name;
29507             } else if (component.types.indexOf("country") >= 0) {
29508                 result.country = component.short_name;
29509             }
29510         }
29511         
29512         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29513         result.addressLine2 = "";
29514         return result;
29515     },
29516     
29517     setZoomLevel: function(zoom)
29518     {
29519         this.gMapContext.map.setZoom(zoom);
29520     },
29521     
29522     show: function()
29523     {
29524         if(!this.el){
29525             return;
29526         }
29527         
29528         this.el.show();
29529         
29530         this.resize();
29531         
29532         this.fireEvent('show', this);
29533     },
29534     
29535     hide: function()
29536     {
29537         if(!this.el){
29538             return;
29539         }
29540         
29541         this.el.hide();
29542         
29543         this.fireEvent('hide', this);
29544     }
29545     
29546 });
29547
29548 Roo.apply(Roo.bootstrap.LocationPicker, {
29549     
29550     OverlayView : function(map, options)
29551     {
29552         options = options || {};
29553         
29554         this.setMap(map);
29555     }
29556     
29557     
29558 });/**
29559  * @class Roo.bootstrap.Alert
29560  * @extends Roo.bootstrap.Component
29561  * Bootstrap Alert class - shows an alert area box
29562  * eg
29563  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29564   Enter a valid email address
29565 </div>
29566  * @licence LGPL
29567  * @cfg {String} title The title of alert
29568  * @cfg {String} html The content of alert
29569  * @cfg {String} weight (  success | info | warning | danger )
29570  * @cfg {String} faicon font-awesomeicon
29571  * 
29572  * @constructor
29573  * Create a new alert
29574  * @param {Object} config The config object
29575  */
29576
29577
29578 Roo.bootstrap.Alert = function(config){
29579     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29580     
29581 };
29582
29583 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29584     
29585     title: '',
29586     html: '',
29587     weight: false,
29588     faicon: false,
29589     
29590     getAutoCreate : function()
29591     {
29592         
29593         var cfg = {
29594             tag : 'div',
29595             cls : 'alert',
29596             cn : [
29597                 {
29598                     tag : 'i',
29599                     cls : 'roo-alert-icon'
29600                     
29601                 },
29602                 {
29603                     tag : 'b',
29604                     cls : 'roo-alert-title',
29605                     html : this.title
29606                 },
29607                 {
29608                     tag : 'span',
29609                     cls : 'roo-alert-text',
29610                     html : this.html
29611                 }
29612             ]
29613         };
29614         
29615         if(this.faicon){
29616             cfg.cn[0].cls += ' fa ' + this.faicon;
29617         }
29618         
29619         if(this.weight){
29620             cfg.cls += ' alert-' + this.weight;
29621         }
29622         
29623         return cfg;
29624     },
29625     
29626     initEvents: function() 
29627     {
29628         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29629     },
29630     
29631     setTitle : function(str)
29632     {
29633         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29634     },
29635     
29636     setText : function(str)
29637     {
29638         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29639     },
29640     
29641     setWeight : function(weight)
29642     {
29643         if(this.weight){
29644             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29645         }
29646         
29647         this.weight = weight;
29648         
29649         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29650     },
29651     
29652     setIcon : function(icon)
29653     {
29654         if(this.faicon){
29655             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29656         }
29657         
29658         this.faicon = icon;
29659         
29660         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29661     },
29662     
29663     hide: function() 
29664     {
29665         this.el.hide();   
29666     },
29667     
29668     show: function() 
29669     {  
29670         this.el.show();   
29671     }
29672     
29673 });
29674
29675  
29676 /*
29677 * Licence: LGPL
29678 */
29679
29680 /**
29681  * @class Roo.bootstrap.UploadCropbox
29682  * @extends Roo.bootstrap.Component
29683  * Bootstrap UploadCropbox class
29684  * @cfg {String} emptyText show when image has been loaded
29685  * @cfg {String} rotateNotify show when image too small to rotate
29686  * @cfg {Number} errorTimeout default 3000
29687  * @cfg {Number} minWidth default 300
29688  * @cfg {Number} minHeight default 300
29689  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29690  * @cfg {Boolean} isDocument (true|false) default false
29691  * @cfg {String} url action url
29692  * @cfg {String} paramName default 'imageUpload'
29693  * @cfg {String} method default POST
29694  * @cfg {Boolean} loadMask (true|false) default true
29695  * @cfg {Boolean} loadingText default 'Loading...'
29696  * 
29697  * @constructor
29698  * Create a new UploadCropbox
29699  * @param {Object} config The config object
29700  */
29701
29702 Roo.bootstrap.UploadCropbox = function(config){
29703     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29704     
29705     this.addEvents({
29706         /**
29707          * @event beforeselectfile
29708          * Fire before select file
29709          * @param {Roo.bootstrap.UploadCropbox} this
29710          */
29711         "beforeselectfile" : true,
29712         /**
29713          * @event initial
29714          * Fire after initEvent
29715          * @param {Roo.bootstrap.UploadCropbox} this
29716          */
29717         "initial" : true,
29718         /**
29719          * @event crop
29720          * Fire after initEvent
29721          * @param {Roo.bootstrap.UploadCropbox} this
29722          * @param {String} data
29723          */
29724         "crop" : true,
29725         /**
29726          * @event prepare
29727          * Fire when preparing the file data
29728          * @param {Roo.bootstrap.UploadCropbox} this
29729          * @param {Object} file
29730          */
29731         "prepare" : true,
29732         /**
29733          * @event exception
29734          * Fire when get exception
29735          * @param {Roo.bootstrap.UploadCropbox} this
29736          * @param {XMLHttpRequest} xhr
29737          */
29738         "exception" : true,
29739         /**
29740          * @event beforeloadcanvas
29741          * Fire before load the canvas
29742          * @param {Roo.bootstrap.UploadCropbox} this
29743          * @param {String} src
29744          */
29745         "beforeloadcanvas" : true,
29746         /**
29747          * @event trash
29748          * Fire when trash image
29749          * @param {Roo.bootstrap.UploadCropbox} this
29750          */
29751         "trash" : true,
29752         /**
29753          * @event download
29754          * Fire when download the image
29755          * @param {Roo.bootstrap.UploadCropbox} this
29756          */
29757         "download" : true,
29758         /**
29759          * @event footerbuttonclick
29760          * Fire when footerbuttonclick
29761          * @param {Roo.bootstrap.UploadCropbox} this
29762          * @param {String} type
29763          */
29764         "footerbuttonclick" : true,
29765         /**
29766          * @event resize
29767          * Fire when resize
29768          * @param {Roo.bootstrap.UploadCropbox} this
29769          */
29770         "resize" : true,
29771         /**
29772          * @event rotate
29773          * Fire when rotate the image
29774          * @param {Roo.bootstrap.UploadCropbox} this
29775          * @param {String} pos
29776          */
29777         "rotate" : true,
29778         /**
29779          * @event inspect
29780          * Fire when inspect the file
29781          * @param {Roo.bootstrap.UploadCropbox} this
29782          * @param {Object} file
29783          */
29784         "inspect" : true,
29785         /**
29786          * @event upload
29787          * Fire when xhr upload the file
29788          * @param {Roo.bootstrap.UploadCropbox} this
29789          * @param {Object} data
29790          */
29791         "upload" : true,
29792         /**
29793          * @event arrange
29794          * Fire when arrange the file data
29795          * @param {Roo.bootstrap.UploadCropbox} this
29796          * @param {Object} formData
29797          */
29798         "arrange" : true
29799     });
29800     
29801     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29802 };
29803
29804 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29805     
29806     emptyText : 'Click to upload image',
29807     rotateNotify : 'Image is too small to rotate',
29808     errorTimeout : 3000,
29809     scale : 0,
29810     baseScale : 1,
29811     rotate : 0,
29812     dragable : false,
29813     pinching : false,
29814     mouseX : 0,
29815     mouseY : 0,
29816     cropData : false,
29817     minWidth : 300,
29818     minHeight : 300,
29819     file : false,
29820     exif : {},
29821     baseRotate : 1,
29822     cropType : 'image/jpeg',
29823     buttons : false,
29824     canvasLoaded : false,
29825     isDocument : false,
29826     method : 'POST',
29827     paramName : 'imageUpload',
29828     loadMask : true,
29829     loadingText : 'Loading...',
29830     maskEl : false,
29831     
29832     getAutoCreate : function()
29833     {
29834         var cfg = {
29835             tag : 'div',
29836             cls : 'roo-upload-cropbox',
29837             cn : [
29838                 {
29839                     tag : 'input',
29840                     cls : 'roo-upload-cropbox-selector',
29841                     type : 'file'
29842                 },
29843                 {
29844                     tag : 'div',
29845                     cls : 'roo-upload-cropbox-body',
29846                     style : 'cursor:pointer',
29847                     cn : [
29848                         {
29849                             tag : 'div',
29850                             cls : 'roo-upload-cropbox-preview'
29851                         },
29852                         {
29853                             tag : 'div',
29854                             cls : 'roo-upload-cropbox-thumb'
29855                         },
29856                         {
29857                             tag : 'div',
29858                             cls : 'roo-upload-cropbox-empty-notify',
29859                             html : this.emptyText
29860                         },
29861                         {
29862                             tag : 'div',
29863                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29864                             html : this.rotateNotify
29865                         }
29866                     ]
29867                 },
29868                 {
29869                     tag : 'div',
29870                     cls : 'roo-upload-cropbox-footer',
29871                     cn : {
29872                         tag : 'div',
29873                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29874                         cn : []
29875                     }
29876                 }
29877             ]
29878         };
29879         
29880         return cfg;
29881     },
29882     
29883     onRender : function(ct, position)
29884     {
29885         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29886         
29887         if (this.buttons.length) {
29888             
29889             Roo.each(this.buttons, function(bb) {
29890                 
29891                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29892                 
29893                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29894                 
29895             }, this);
29896         }
29897         
29898         if(this.loadMask){
29899             this.maskEl = this.el;
29900         }
29901     },
29902     
29903     initEvents : function()
29904     {
29905         this.urlAPI = (window.createObjectURL && window) || 
29906                                 (window.URL && URL.revokeObjectURL && URL) || 
29907                                 (window.webkitURL && webkitURL);
29908                         
29909         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29910         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29911         
29912         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29913         this.selectorEl.hide();
29914         
29915         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29916         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29917         
29918         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29919         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29920         this.thumbEl.hide();
29921         
29922         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29923         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29924         
29925         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29926         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29927         this.errorEl.hide();
29928         
29929         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29930         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29931         this.footerEl.hide();
29932         
29933         this.setThumbBoxSize();
29934         
29935         this.bind();
29936         
29937         this.resize();
29938         
29939         this.fireEvent('initial', this);
29940     },
29941
29942     bind : function()
29943     {
29944         var _this = this;
29945         
29946         window.addEventListener("resize", function() { _this.resize(); } );
29947         
29948         this.bodyEl.on('click', this.beforeSelectFile, this);
29949         
29950         if(Roo.isTouch){
29951             this.bodyEl.on('touchstart', this.onTouchStart, this);
29952             this.bodyEl.on('touchmove', this.onTouchMove, this);
29953             this.bodyEl.on('touchend', this.onTouchEnd, this);
29954         }
29955         
29956         if(!Roo.isTouch){
29957             this.bodyEl.on('mousedown', this.onMouseDown, this);
29958             this.bodyEl.on('mousemove', this.onMouseMove, this);
29959             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29960             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29961             Roo.get(document).on('mouseup', this.onMouseUp, this);
29962         }
29963         
29964         this.selectorEl.on('change', this.onFileSelected, this);
29965     },
29966     
29967     reset : function()
29968     {    
29969         this.scale = 0;
29970         this.baseScale = 1;
29971         this.rotate = 0;
29972         this.baseRotate = 1;
29973         this.dragable = false;
29974         this.pinching = false;
29975         this.mouseX = 0;
29976         this.mouseY = 0;
29977         this.cropData = false;
29978         this.notifyEl.dom.innerHTML = this.emptyText;
29979         
29980         this.selectorEl.dom.value = '';
29981         
29982     },
29983     
29984     resize : function()
29985     {
29986         if(this.fireEvent('resize', this) != false){
29987             this.setThumbBoxPosition();
29988             this.setCanvasPosition();
29989         }
29990     },
29991     
29992     onFooterButtonClick : function(e, el, o, type)
29993     {
29994         switch (type) {
29995             case 'rotate-left' :
29996                 this.onRotateLeft(e);
29997                 break;
29998             case 'rotate-right' :
29999                 this.onRotateRight(e);
30000                 break;
30001             case 'picture' :
30002                 this.beforeSelectFile(e);
30003                 break;
30004             case 'trash' :
30005                 this.trash(e);
30006                 break;
30007             case 'crop' :
30008                 this.crop(e);
30009                 break;
30010             case 'download' :
30011                 this.download(e);
30012                 break;
30013             default :
30014                 break;
30015         }
30016         
30017         this.fireEvent('footerbuttonclick', this, type);
30018     },
30019     
30020     beforeSelectFile : function(e)
30021     {
30022         e.preventDefault();
30023         
30024         if(this.fireEvent('beforeselectfile', this) != false){
30025             this.selectorEl.dom.click();
30026         }
30027     },
30028     
30029     onFileSelected : function(e)
30030     {
30031         e.preventDefault();
30032         
30033         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30034             return;
30035         }
30036         
30037         var file = this.selectorEl.dom.files[0];
30038         
30039         if(this.fireEvent('inspect', this, file) != false){
30040             this.prepare(file);
30041         }
30042         
30043     },
30044     
30045     trash : function(e)
30046     {
30047         this.fireEvent('trash', this);
30048     },
30049     
30050     download : function(e)
30051     {
30052         this.fireEvent('download', this);
30053     },
30054     
30055     loadCanvas : function(src)
30056     {   
30057         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30058             
30059             this.reset();
30060             
30061             this.imageEl = document.createElement('img');
30062             
30063             var _this = this;
30064             
30065             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30066             
30067             this.imageEl.src = src;
30068         }
30069     },
30070     
30071     onLoadCanvas : function()
30072     {   
30073         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30074         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30075         
30076         this.bodyEl.un('click', this.beforeSelectFile, this);
30077         
30078         this.notifyEl.hide();
30079         this.thumbEl.show();
30080         this.footerEl.show();
30081         
30082         this.baseRotateLevel();
30083         
30084         if(this.isDocument){
30085             this.setThumbBoxSize();
30086         }
30087         
30088         this.setThumbBoxPosition();
30089         
30090         this.baseScaleLevel();
30091         
30092         this.draw();
30093         
30094         this.resize();
30095         
30096         this.canvasLoaded = true;
30097         
30098         if(this.loadMask){
30099             this.maskEl.unmask();
30100         }
30101         
30102     },
30103     
30104     setCanvasPosition : function()
30105     {   
30106         if(!this.canvasEl){
30107             return;
30108         }
30109         
30110         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30111         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30112         
30113         this.previewEl.setLeft(pw);
30114         this.previewEl.setTop(ph);
30115         
30116     },
30117     
30118     onMouseDown : function(e)
30119     {   
30120         e.stopEvent();
30121         
30122         this.dragable = true;
30123         this.pinching = false;
30124         
30125         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30126             this.dragable = false;
30127             return;
30128         }
30129         
30130         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30131         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30132         
30133     },
30134     
30135     onMouseMove : function(e)
30136     {   
30137         e.stopEvent();
30138         
30139         if(!this.canvasLoaded){
30140             return;
30141         }
30142         
30143         if (!this.dragable){
30144             return;
30145         }
30146         
30147         var minX = Math.ceil(this.thumbEl.getLeft(true));
30148         var minY = Math.ceil(this.thumbEl.getTop(true));
30149         
30150         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30151         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30152         
30153         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30154         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30155         
30156         x = x - this.mouseX;
30157         y = y - this.mouseY;
30158         
30159         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30160         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30161         
30162         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30163         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30164         
30165         this.previewEl.setLeft(bgX);
30166         this.previewEl.setTop(bgY);
30167         
30168         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30169         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30170     },
30171     
30172     onMouseUp : function(e)
30173     {   
30174         e.stopEvent();
30175         
30176         this.dragable = false;
30177     },
30178     
30179     onMouseWheel : function(e)
30180     {   
30181         e.stopEvent();
30182         
30183         this.startScale = this.scale;
30184         
30185         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30186         
30187         if(!this.zoomable()){
30188             this.scale = this.startScale;
30189             return;
30190         }
30191         
30192         this.draw();
30193         
30194         return;
30195     },
30196     
30197     zoomable : function()
30198     {
30199         var minScale = this.thumbEl.getWidth() / this.minWidth;
30200         
30201         if(this.minWidth < this.minHeight){
30202             minScale = this.thumbEl.getHeight() / this.minHeight;
30203         }
30204         
30205         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30206         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30207         
30208         if(
30209                 this.isDocument &&
30210                 (this.rotate == 0 || this.rotate == 180) && 
30211                 (
30212                     width > this.imageEl.OriginWidth || 
30213                     height > this.imageEl.OriginHeight ||
30214                     (width < this.minWidth && height < this.minHeight)
30215                 )
30216         ){
30217             return false;
30218         }
30219         
30220         if(
30221                 this.isDocument &&
30222                 (this.rotate == 90 || this.rotate == 270) && 
30223                 (
30224                     width > this.imageEl.OriginWidth || 
30225                     height > this.imageEl.OriginHeight ||
30226                     (width < this.minHeight && height < this.minWidth)
30227                 )
30228         ){
30229             return false;
30230         }
30231         
30232         if(
30233                 !this.isDocument &&
30234                 (this.rotate == 0 || this.rotate == 180) && 
30235                 (
30236                     width < this.minWidth || 
30237                     width > this.imageEl.OriginWidth || 
30238                     height < this.minHeight || 
30239                     height > this.imageEl.OriginHeight
30240                 )
30241         ){
30242             return false;
30243         }
30244         
30245         if(
30246                 !this.isDocument &&
30247                 (this.rotate == 90 || this.rotate == 270) && 
30248                 (
30249                     width < this.minHeight || 
30250                     width > this.imageEl.OriginWidth || 
30251                     height < this.minWidth || 
30252                     height > this.imageEl.OriginHeight
30253                 )
30254         ){
30255             return false;
30256         }
30257         
30258         return true;
30259         
30260     },
30261     
30262     onRotateLeft : function(e)
30263     {   
30264         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30265             
30266             var minScale = this.thumbEl.getWidth() / this.minWidth;
30267             
30268             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30269             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30270             
30271             this.startScale = this.scale;
30272             
30273             while (this.getScaleLevel() < minScale){
30274             
30275                 this.scale = this.scale + 1;
30276                 
30277                 if(!this.zoomable()){
30278                     break;
30279                 }
30280                 
30281                 if(
30282                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30283                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30284                 ){
30285                     continue;
30286                 }
30287                 
30288                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30289
30290                 this.draw();
30291                 
30292                 return;
30293             }
30294             
30295             this.scale = this.startScale;
30296             
30297             this.onRotateFail();
30298             
30299             return false;
30300         }
30301         
30302         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30303
30304         if(this.isDocument){
30305             this.setThumbBoxSize();
30306             this.setThumbBoxPosition();
30307             this.setCanvasPosition();
30308         }
30309         
30310         this.draw();
30311         
30312         this.fireEvent('rotate', this, 'left');
30313         
30314     },
30315     
30316     onRotateRight : function(e)
30317     {
30318         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30319             
30320             var minScale = this.thumbEl.getWidth() / this.minWidth;
30321         
30322             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30323             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30324             
30325             this.startScale = this.scale;
30326             
30327             while (this.getScaleLevel() < minScale){
30328             
30329                 this.scale = this.scale + 1;
30330                 
30331                 if(!this.zoomable()){
30332                     break;
30333                 }
30334                 
30335                 if(
30336                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30337                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30338                 ){
30339                     continue;
30340                 }
30341                 
30342                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30343
30344                 this.draw();
30345                 
30346                 return;
30347             }
30348             
30349             this.scale = this.startScale;
30350             
30351             this.onRotateFail();
30352             
30353             return false;
30354         }
30355         
30356         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30357
30358         if(this.isDocument){
30359             this.setThumbBoxSize();
30360             this.setThumbBoxPosition();
30361             this.setCanvasPosition();
30362         }
30363         
30364         this.draw();
30365         
30366         this.fireEvent('rotate', this, 'right');
30367     },
30368     
30369     onRotateFail : function()
30370     {
30371         this.errorEl.show(true);
30372         
30373         var _this = this;
30374         
30375         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30376     },
30377     
30378     draw : function()
30379     {
30380         this.previewEl.dom.innerHTML = '';
30381         
30382         var canvasEl = document.createElement("canvas");
30383         
30384         var contextEl = canvasEl.getContext("2d");
30385         
30386         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30387         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30388         var center = this.imageEl.OriginWidth / 2;
30389         
30390         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30391             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30392             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30393             center = this.imageEl.OriginHeight / 2;
30394         }
30395         
30396         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30397         
30398         contextEl.translate(center, center);
30399         contextEl.rotate(this.rotate * Math.PI / 180);
30400
30401         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30402         
30403         this.canvasEl = document.createElement("canvas");
30404         
30405         this.contextEl = this.canvasEl.getContext("2d");
30406         
30407         switch (this.rotate) {
30408             case 0 :
30409                 
30410                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30411                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30412                 
30413                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30414                 
30415                 break;
30416             case 90 : 
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, Math.abs(this.canvasEl.width - this.canvasEl.height), 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, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30427                 
30428                 break;
30429             case 180 :
30430                 
30431                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30432                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30433                 
30434                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30435                     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);
30436                     break;
30437                 }
30438                 
30439                 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);
30440                 
30441                 break;
30442             case 270 :
30443                 
30444                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30445                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30446         
30447                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30448                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30449                     break;
30450                 }
30451                 
30452                 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);
30453                 
30454                 break;
30455             default : 
30456                 break;
30457         }
30458         
30459         this.previewEl.appendChild(this.canvasEl);
30460         
30461         this.setCanvasPosition();
30462     },
30463     
30464     crop : function()
30465     {
30466         if(!this.canvasLoaded){
30467             return;
30468         }
30469         
30470         var imageCanvas = document.createElement("canvas");
30471         
30472         var imageContext = imageCanvas.getContext("2d");
30473         
30474         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30475         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30476         
30477         var center = imageCanvas.width / 2;
30478         
30479         imageContext.translate(center, center);
30480         
30481         imageContext.rotate(this.rotate * Math.PI / 180);
30482         
30483         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30484         
30485         var canvas = document.createElement("canvas");
30486         
30487         var context = canvas.getContext("2d");
30488                 
30489         canvas.width = this.minWidth;
30490         canvas.height = this.minHeight;
30491
30492         switch (this.rotate) {
30493             case 0 :
30494                 
30495                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30496                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30497                 
30498                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30499                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30500                 
30501                 var targetWidth = this.minWidth - 2 * x;
30502                 var targetHeight = this.minHeight - 2 * y;
30503                 
30504                 var scale = 1;
30505                 
30506                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30507                     scale = targetWidth / width;
30508                 }
30509                 
30510                 if(x > 0 && y == 0){
30511                     scale = targetHeight / height;
30512                 }
30513                 
30514                 if(x > 0 && y > 0){
30515                     scale = targetWidth / width;
30516                     
30517                     if(width < height){
30518                         scale = targetHeight / height;
30519                     }
30520                 }
30521                 
30522                 context.scale(scale, scale);
30523                 
30524                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30525                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30526
30527                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30528                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30529
30530                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30531                 
30532                 break;
30533             case 90 : 
30534                 
30535                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30536                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30537                 
30538                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30539                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30540                 
30541                 var targetWidth = this.minWidth - 2 * x;
30542                 var targetHeight = this.minHeight - 2 * y;
30543                 
30544                 var scale = 1;
30545                 
30546                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30547                     scale = targetWidth / width;
30548                 }
30549                 
30550                 if(x > 0 && y == 0){
30551                     scale = targetHeight / height;
30552                 }
30553                 
30554                 if(x > 0 && y > 0){
30555                     scale = targetWidth / width;
30556                     
30557                     if(width < height){
30558                         scale = targetHeight / height;
30559                     }
30560                 }
30561                 
30562                 context.scale(scale, scale);
30563                 
30564                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30565                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30566
30567                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30568                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30569                 
30570                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30571                 
30572                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30573                 
30574                 break;
30575             case 180 :
30576                 
30577                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30578                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30579                 
30580                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30581                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30582                 
30583                 var targetWidth = this.minWidth - 2 * x;
30584                 var targetHeight = this.minHeight - 2 * y;
30585                 
30586                 var scale = 1;
30587                 
30588                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30589                     scale = targetWidth / width;
30590                 }
30591                 
30592                 if(x > 0 && y == 0){
30593                     scale = targetHeight / height;
30594                 }
30595                 
30596                 if(x > 0 && y > 0){
30597                     scale = targetWidth / width;
30598                     
30599                     if(width < height){
30600                         scale = targetHeight / height;
30601                     }
30602                 }
30603                 
30604                 context.scale(scale, scale);
30605                 
30606                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30607                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30608
30609                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30610                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30611
30612                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30613                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30614                 
30615                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30616                 
30617                 break;
30618             case 270 :
30619                 
30620                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30621                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30622                 
30623                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30624                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30625                 
30626                 var targetWidth = this.minWidth - 2 * x;
30627                 var targetHeight = this.minHeight - 2 * y;
30628                 
30629                 var scale = 1;
30630                 
30631                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30632                     scale = targetWidth / width;
30633                 }
30634                 
30635                 if(x > 0 && y == 0){
30636                     scale = targetHeight / height;
30637                 }
30638                 
30639                 if(x > 0 && y > 0){
30640                     scale = targetWidth / width;
30641                     
30642                     if(width < height){
30643                         scale = targetHeight / height;
30644                     }
30645                 }
30646                 
30647                 context.scale(scale, scale);
30648                 
30649                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30650                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30651
30652                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30653                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30654                 
30655                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30656                 
30657                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30658                 
30659                 break;
30660             default : 
30661                 break;
30662         }
30663         
30664         this.cropData = canvas.toDataURL(this.cropType);
30665         
30666         if(this.fireEvent('crop', this, this.cropData) !== false){
30667             this.process(this.file, this.cropData);
30668         }
30669         
30670         return;
30671         
30672     },
30673     
30674     setThumbBoxSize : function()
30675     {
30676         var width, height;
30677         
30678         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30679             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30680             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30681             
30682             this.minWidth = width;
30683             this.minHeight = height;
30684             
30685             if(this.rotate == 90 || this.rotate == 270){
30686                 this.minWidth = height;
30687                 this.minHeight = width;
30688             }
30689         }
30690         
30691         height = 300;
30692         width = Math.ceil(this.minWidth * height / this.minHeight);
30693         
30694         if(this.minWidth > this.minHeight){
30695             width = 300;
30696             height = Math.ceil(this.minHeight * width / this.minWidth);
30697         }
30698         
30699         this.thumbEl.setStyle({
30700             width : width + 'px',
30701             height : height + 'px'
30702         });
30703
30704         return;
30705             
30706     },
30707     
30708     setThumbBoxPosition : function()
30709     {
30710         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30711         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30712         
30713         this.thumbEl.setLeft(x);
30714         this.thumbEl.setTop(y);
30715         
30716     },
30717     
30718     baseRotateLevel : function()
30719     {
30720         this.baseRotate = 1;
30721         
30722         if(
30723                 typeof(this.exif) != 'undefined' &&
30724                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30725                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30726         ){
30727             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30728         }
30729         
30730         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30731         
30732     },
30733     
30734     baseScaleLevel : function()
30735     {
30736         var width, height;
30737         
30738         if(this.isDocument){
30739             
30740             if(this.baseRotate == 6 || this.baseRotate == 8){
30741             
30742                 height = this.thumbEl.getHeight();
30743                 this.baseScale = height / this.imageEl.OriginWidth;
30744
30745                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30746                     width = this.thumbEl.getWidth();
30747                     this.baseScale = width / this.imageEl.OriginHeight;
30748                 }
30749
30750                 return;
30751             }
30752
30753             height = this.thumbEl.getHeight();
30754             this.baseScale = height / this.imageEl.OriginHeight;
30755
30756             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30757                 width = this.thumbEl.getWidth();
30758                 this.baseScale = width / this.imageEl.OriginWidth;
30759             }
30760
30761             return;
30762         }
30763         
30764         if(this.baseRotate == 6 || this.baseRotate == 8){
30765             
30766             width = this.thumbEl.getHeight();
30767             this.baseScale = width / this.imageEl.OriginHeight;
30768             
30769             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30770                 height = this.thumbEl.getWidth();
30771                 this.baseScale = height / this.imageEl.OriginHeight;
30772             }
30773             
30774             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30775                 height = this.thumbEl.getWidth();
30776                 this.baseScale = height / this.imageEl.OriginHeight;
30777                 
30778                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30779                     width = this.thumbEl.getHeight();
30780                     this.baseScale = width / this.imageEl.OriginWidth;
30781                 }
30782             }
30783             
30784             return;
30785         }
30786         
30787         width = this.thumbEl.getWidth();
30788         this.baseScale = width / this.imageEl.OriginWidth;
30789         
30790         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30791             height = this.thumbEl.getHeight();
30792             this.baseScale = height / this.imageEl.OriginHeight;
30793         }
30794         
30795         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30796             
30797             height = this.thumbEl.getHeight();
30798             this.baseScale = height / this.imageEl.OriginHeight;
30799             
30800             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30801                 width = this.thumbEl.getWidth();
30802                 this.baseScale = width / this.imageEl.OriginWidth;
30803             }
30804             
30805         }
30806         
30807         return;
30808     },
30809     
30810     getScaleLevel : function()
30811     {
30812         return this.baseScale * Math.pow(1.1, this.scale);
30813     },
30814     
30815     onTouchStart : function(e)
30816     {
30817         if(!this.canvasLoaded){
30818             this.beforeSelectFile(e);
30819             return;
30820         }
30821         
30822         var touches = e.browserEvent.touches;
30823         
30824         if(!touches){
30825             return;
30826         }
30827         
30828         if(touches.length == 1){
30829             this.onMouseDown(e);
30830             return;
30831         }
30832         
30833         if(touches.length != 2){
30834             return;
30835         }
30836         
30837         var coords = [];
30838         
30839         for(var i = 0, finger; finger = touches[i]; i++){
30840             coords.push(finger.pageX, finger.pageY);
30841         }
30842         
30843         var x = Math.pow(coords[0] - coords[2], 2);
30844         var y = Math.pow(coords[1] - coords[3], 2);
30845         
30846         this.startDistance = Math.sqrt(x + y);
30847         
30848         this.startScale = this.scale;
30849         
30850         this.pinching = true;
30851         this.dragable = false;
30852         
30853     },
30854     
30855     onTouchMove : function(e)
30856     {
30857         if(!this.pinching && !this.dragable){
30858             return;
30859         }
30860         
30861         var touches = e.browserEvent.touches;
30862         
30863         if(!touches){
30864             return;
30865         }
30866         
30867         if(this.dragable){
30868             this.onMouseMove(e);
30869             return;
30870         }
30871         
30872         var coords = [];
30873         
30874         for(var i = 0, finger; finger = touches[i]; i++){
30875             coords.push(finger.pageX, finger.pageY);
30876         }
30877         
30878         var x = Math.pow(coords[0] - coords[2], 2);
30879         var y = Math.pow(coords[1] - coords[3], 2);
30880         
30881         this.endDistance = Math.sqrt(x + y);
30882         
30883         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30884         
30885         if(!this.zoomable()){
30886             this.scale = this.startScale;
30887             return;
30888         }
30889         
30890         this.draw();
30891         
30892     },
30893     
30894     onTouchEnd : function(e)
30895     {
30896         this.pinching = false;
30897         this.dragable = false;
30898         
30899     },
30900     
30901     process : function(file, crop)
30902     {
30903         if(this.loadMask){
30904             this.maskEl.mask(this.loadingText);
30905         }
30906         
30907         this.xhr = new XMLHttpRequest();
30908         
30909         file.xhr = this.xhr;
30910
30911         this.xhr.open(this.method, this.url, true);
30912         
30913         var headers = {
30914             "Accept": "application/json",
30915             "Cache-Control": "no-cache",
30916             "X-Requested-With": "XMLHttpRequest"
30917         };
30918         
30919         for (var headerName in headers) {
30920             var headerValue = headers[headerName];
30921             if (headerValue) {
30922                 this.xhr.setRequestHeader(headerName, headerValue);
30923             }
30924         }
30925         
30926         var _this = this;
30927         
30928         this.xhr.onload = function()
30929         {
30930             _this.xhrOnLoad(_this.xhr);
30931         }
30932         
30933         this.xhr.onerror = function()
30934         {
30935             _this.xhrOnError(_this.xhr);
30936         }
30937         
30938         var formData = new FormData();
30939
30940         formData.append('returnHTML', 'NO');
30941         
30942         if(crop){
30943             formData.append('crop', crop);
30944         }
30945         
30946         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30947             formData.append(this.paramName, file, file.name);
30948         }
30949         
30950         if(typeof(file.filename) != 'undefined'){
30951             formData.append('filename', file.filename);
30952         }
30953         
30954         if(typeof(file.mimetype) != 'undefined'){
30955             formData.append('mimetype', file.mimetype);
30956         }
30957         
30958         if(this.fireEvent('arrange', this, formData) != false){
30959             this.xhr.send(formData);
30960         };
30961     },
30962     
30963     xhrOnLoad : function(xhr)
30964     {
30965         if(this.loadMask){
30966             this.maskEl.unmask();
30967         }
30968         
30969         if (xhr.readyState !== 4) {
30970             this.fireEvent('exception', this, xhr);
30971             return;
30972         }
30973
30974         var response = Roo.decode(xhr.responseText);
30975         
30976         if(!response.success){
30977             this.fireEvent('exception', this, xhr);
30978             return;
30979         }
30980         
30981         var response = Roo.decode(xhr.responseText);
30982         
30983         this.fireEvent('upload', this, response);
30984         
30985     },
30986     
30987     xhrOnError : function()
30988     {
30989         if(this.loadMask){
30990             this.maskEl.unmask();
30991         }
30992         
30993         Roo.log('xhr on error');
30994         
30995         var response = Roo.decode(xhr.responseText);
30996           
30997         Roo.log(response);
30998         
30999     },
31000     
31001     prepare : function(file)
31002     {   
31003         if(this.loadMask){
31004             this.maskEl.mask(this.loadingText);
31005         }
31006         
31007         this.file = false;
31008         this.exif = {};
31009         
31010         if(typeof(file) === 'string'){
31011             this.loadCanvas(file);
31012             return;
31013         }
31014         
31015         if(!file || !this.urlAPI){
31016             return;
31017         }
31018         
31019         this.file = file;
31020         this.cropType = file.type;
31021         
31022         var _this = this;
31023         
31024         if(this.fireEvent('prepare', this, this.file) != false){
31025             
31026             var reader = new FileReader();
31027             
31028             reader.onload = function (e) {
31029                 if (e.target.error) {
31030                     Roo.log(e.target.error);
31031                     return;
31032                 }
31033                 
31034                 var buffer = e.target.result,
31035                     dataView = new DataView(buffer),
31036                     offset = 2,
31037                     maxOffset = dataView.byteLength - 4,
31038                     markerBytes,
31039                     markerLength;
31040                 
31041                 if (dataView.getUint16(0) === 0xffd8) {
31042                     while (offset < maxOffset) {
31043                         markerBytes = dataView.getUint16(offset);
31044                         
31045                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31046                             markerLength = dataView.getUint16(offset + 2) + 2;
31047                             if (offset + markerLength > dataView.byteLength) {
31048                                 Roo.log('Invalid meta data: Invalid segment size.');
31049                                 break;
31050                             }
31051                             
31052                             if(markerBytes == 0xffe1){
31053                                 _this.parseExifData(
31054                                     dataView,
31055                                     offset,
31056                                     markerLength
31057                                 );
31058                             }
31059                             
31060                             offset += markerLength;
31061                             
31062                             continue;
31063                         }
31064                         
31065                         break;
31066                     }
31067                     
31068                 }
31069                 
31070                 var url = _this.urlAPI.createObjectURL(_this.file);
31071                 
31072                 _this.loadCanvas(url);
31073                 
31074                 return;
31075             }
31076             
31077             reader.readAsArrayBuffer(this.file);
31078             
31079         }
31080         
31081     },
31082     
31083     parseExifData : function(dataView, offset, length)
31084     {
31085         var tiffOffset = offset + 10,
31086             littleEndian,
31087             dirOffset;
31088     
31089         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31090             // No Exif data, might be XMP data instead
31091             return;
31092         }
31093         
31094         // Check for the ASCII code for "Exif" (0x45786966):
31095         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31096             // No Exif data, might be XMP data instead
31097             return;
31098         }
31099         if (tiffOffset + 8 > dataView.byteLength) {
31100             Roo.log('Invalid Exif data: Invalid segment size.');
31101             return;
31102         }
31103         // Check for the two null bytes:
31104         if (dataView.getUint16(offset + 8) !== 0x0000) {
31105             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31106             return;
31107         }
31108         // Check the byte alignment:
31109         switch (dataView.getUint16(tiffOffset)) {
31110         case 0x4949:
31111             littleEndian = true;
31112             break;
31113         case 0x4D4D:
31114             littleEndian = false;
31115             break;
31116         default:
31117             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31118             return;
31119         }
31120         // Check for the TIFF tag marker (0x002A):
31121         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31122             Roo.log('Invalid Exif data: Missing TIFF marker.');
31123             return;
31124         }
31125         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31126         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31127         
31128         this.parseExifTags(
31129             dataView,
31130             tiffOffset,
31131             tiffOffset + dirOffset,
31132             littleEndian
31133         );
31134     },
31135     
31136     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31137     {
31138         var tagsNumber,
31139             dirEndOffset,
31140             i;
31141         if (dirOffset + 6 > dataView.byteLength) {
31142             Roo.log('Invalid Exif data: Invalid directory offset.');
31143             return;
31144         }
31145         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31146         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31147         if (dirEndOffset + 4 > dataView.byteLength) {
31148             Roo.log('Invalid Exif data: Invalid directory size.');
31149             return;
31150         }
31151         for (i = 0; i < tagsNumber; i += 1) {
31152             this.parseExifTag(
31153                 dataView,
31154                 tiffOffset,
31155                 dirOffset + 2 + 12 * i, // tag offset
31156                 littleEndian
31157             );
31158         }
31159         // Return the offset to the next directory:
31160         return dataView.getUint32(dirEndOffset, littleEndian);
31161     },
31162     
31163     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31164     {
31165         var tag = dataView.getUint16(offset, littleEndian);
31166         
31167         this.exif[tag] = this.getExifValue(
31168             dataView,
31169             tiffOffset,
31170             offset,
31171             dataView.getUint16(offset + 2, littleEndian), // tag type
31172             dataView.getUint32(offset + 4, littleEndian), // tag length
31173             littleEndian
31174         );
31175     },
31176     
31177     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31178     {
31179         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31180             tagSize,
31181             dataOffset,
31182             values,
31183             i,
31184             str,
31185             c;
31186     
31187         if (!tagType) {
31188             Roo.log('Invalid Exif data: Invalid tag type.');
31189             return;
31190         }
31191         
31192         tagSize = tagType.size * length;
31193         // Determine if the value is contained in the dataOffset bytes,
31194         // or if the value at the dataOffset is a pointer to the actual data:
31195         dataOffset = tagSize > 4 ?
31196                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31197         if (dataOffset + tagSize > dataView.byteLength) {
31198             Roo.log('Invalid Exif data: Invalid data offset.');
31199             return;
31200         }
31201         if (length === 1) {
31202             return tagType.getValue(dataView, dataOffset, littleEndian);
31203         }
31204         values = [];
31205         for (i = 0; i < length; i += 1) {
31206             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31207         }
31208         
31209         if (tagType.ascii) {
31210             str = '';
31211             // Concatenate the chars:
31212             for (i = 0; i < values.length; i += 1) {
31213                 c = values[i];
31214                 // Ignore the terminating NULL byte(s):
31215                 if (c === '\u0000') {
31216                     break;
31217                 }
31218                 str += c;
31219             }
31220             return str;
31221         }
31222         return values;
31223     }
31224     
31225 });
31226
31227 Roo.apply(Roo.bootstrap.UploadCropbox, {
31228     tags : {
31229         'Orientation': 0x0112
31230     },
31231     
31232     Orientation: {
31233             1: 0, //'top-left',
31234 //            2: 'top-right',
31235             3: 180, //'bottom-right',
31236 //            4: 'bottom-left',
31237 //            5: 'left-top',
31238             6: 90, //'right-top',
31239 //            7: 'right-bottom',
31240             8: 270 //'left-bottom'
31241     },
31242     
31243     exifTagTypes : {
31244         // byte, 8-bit unsigned int:
31245         1: {
31246             getValue: function (dataView, dataOffset) {
31247                 return dataView.getUint8(dataOffset);
31248             },
31249             size: 1
31250         },
31251         // ascii, 8-bit byte:
31252         2: {
31253             getValue: function (dataView, dataOffset) {
31254                 return String.fromCharCode(dataView.getUint8(dataOffset));
31255             },
31256             size: 1,
31257             ascii: true
31258         },
31259         // short, 16 bit int:
31260         3: {
31261             getValue: function (dataView, dataOffset, littleEndian) {
31262                 return dataView.getUint16(dataOffset, littleEndian);
31263             },
31264             size: 2
31265         },
31266         // long, 32 bit int:
31267         4: {
31268             getValue: function (dataView, dataOffset, littleEndian) {
31269                 return dataView.getUint32(dataOffset, littleEndian);
31270             },
31271             size: 4
31272         },
31273         // rational = two long values, first is numerator, second is denominator:
31274         5: {
31275             getValue: function (dataView, dataOffset, littleEndian) {
31276                 return dataView.getUint32(dataOffset, littleEndian) /
31277                     dataView.getUint32(dataOffset + 4, littleEndian);
31278             },
31279             size: 8
31280         },
31281         // slong, 32 bit signed int:
31282         9: {
31283             getValue: function (dataView, dataOffset, littleEndian) {
31284                 return dataView.getInt32(dataOffset, littleEndian);
31285             },
31286             size: 4
31287         },
31288         // srational, two slongs, first is numerator, second is denominator:
31289         10: {
31290             getValue: function (dataView, dataOffset, littleEndian) {
31291                 return dataView.getInt32(dataOffset, littleEndian) /
31292                     dataView.getInt32(dataOffset + 4, littleEndian);
31293             },
31294             size: 8
31295         }
31296     },
31297     
31298     footer : {
31299         STANDARD : [
31300             {
31301                 tag : 'div',
31302                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31303                 action : 'rotate-left',
31304                 cn : [
31305                     {
31306                         tag : 'button',
31307                         cls : 'btn btn-default',
31308                         html : '<i class="fa fa-undo"></i>'
31309                     }
31310                 ]
31311             },
31312             {
31313                 tag : 'div',
31314                 cls : 'btn-group roo-upload-cropbox-picture',
31315                 action : 'picture',
31316                 cn : [
31317                     {
31318                         tag : 'button',
31319                         cls : 'btn btn-default',
31320                         html : '<i class="fa fa-picture-o"></i>'
31321                     }
31322                 ]
31323             },
31324             {
31325                 tag : 'div',
31326                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31327                 action : 'rotate-right',
31328                 cn : [
31329                     {
31330                         tag : 'button',
31331                         cls : 'btn btn-default',
31332                         html : '<i class="fa fa-repeat"></i>'
31333                     }
31334                 ]
31335             }
31336         ],
31337         DOCUMENT : [
31338             {
31339                 tag : 'div',
31340                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31341                 action : 'rotate-left',
31342                 cn : [
31343                     {
31344                         tag : 'button',
31345                         cls : 'btn btn-default',
31346                         html : '<i class="fa fa-undo"></i>'
31347                     }
31348                 ]
31349             },
31350             {
31351                 tag : 'div',
31352                 cls : 'btn-group roo-upload-cropbox-download',
31353                 action : 'download',
31354                 cn : [
31355                     {
31356                         tag : 'button',
31357                         cls : 'btn btn-default',
31358                         html : '<i class="fa fa-download"></i>'
31359                     }
31360                 ]
31361             },
31362             {
31363                 tag : 'div',
31364                 cls : 'btn-group roo-upload-cropbox-crop',
31365                 action : 'crop',
31366                 cn : [
31367                     {
31368                         tag : 'button',
31369                         cls : 'btn btn-default',
31370                         html : '<i class="fa fa-crop"></i>'
31371                     }
31372                 ]
31373             },
31374             {
31375                 tag : 'div',
31376                 cls : 'btn-group roo-upload-cropbox-trash',
31377                 action : 'trash',
31378                 cn : [
31379                     {
31380                         tag : 'button',
31381                         cls : 'btn btn-default',
31382                         html : '<i class="fa fa-trash"></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         ROTATOR : [
31400             {
31401                 tag : 'div',
31402                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31403                 action : 'rotate-left',
31404                 cn : [
31405                     {
31406                         tag : 'button',
31407                         cls : 'btn btn-default',
31408                         html : '<i class="fa fa-undo"></i>'
31409                     }
31410                 ]
31411             },
31412             {
31413                 tag : 'div',
31414                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31415                 action : 'rotate-right',
31416                 cn : [
31417                     {
31418                         tag : 'button',
31419                         cls : 'btn btn-default',
31420                         html : '<i class="fa fa-repeat"></i>'
31421                     }
31422                 ]
31423             }
31424         ]
31425     }
31426 });
31427
31428 /*
31429 * Licence: LGPL
31430 */
31431
31432 /**
31433  * @class Roo.bootstrap.DocumentManager
31434  * @extends Roo.bootstrap.Component
31435  * Bootstrap DocumentManager class
31436  * @cfg {String} paramName default 'imageUpload'
31437  * @cfg {String} toolTipName default 'filename'
31438  * @cfg {String} method default POST
31439  * @cfg {String} url action url
31440  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31441  * @cfg {Boolean} multiple multiple upload default true
31442  * @cfg {Number} thumbSize default 300
31443  * @cfg {String} fieldLabel
31444  * @cfg {Number} labelWidth default 4
31445  * @cfg {String} labelAlign (left|top) default left
31446  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31447 * @cfg {Number} labellg set the width of label (1-12)
31448  * @cfg {Number} labelmd set the width of label (1-12)
31449  * @cfg {Number} labelsm set the width of label (1-12)
31450  * @cfg {Number} labelxs set the width of label (1-12)
31451  * 
31452  * @constructor
31453  * Create a new DocumentManager
31454  * @param {Object} config The config object
31455  */
31456
31457 Roo.bootstrap.DocumentManager = function(config){
31458     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31459     
31460     this.files = [];
31461     this.delegates = [];
31462     
31463     this.addEvents({
31464         /**
31465          * @event initial
31466          * Fire when initial the DocumentManager
31467          * @param {Roo.bootstrap.DocumentManager} this
31468          */
31469         "initial" : true,
31470         /**
31471          * @event inspect
31472          * inspect selected file
31473          * @param {Roo.bootstrap.DocumentManager} this
31474          * @param {File} file
31475          */
31476         "inspect" : true,
31477         /**
31478          * @event exception
31479          * Fire when xhr load exception
31480          * @param {Roo.bootstrap.DocumentManager} this
31481          * @param {XMLHttpRequest} xhr
31482          */
31483         "exception" : true,
31484         /**
31485          * @event afterupload
31486          * Fire when xhr load exception
31487          * @param {Roo.bootstrap.DocumentManager} this
31488          * @param {XMLHttpRequest} xhr
31489          */
31490         "afterupload" : true,
31491         /**
31492          * @event prepare
31493          * prepare the form data
31494          * @param {Roo.bootstrap.DocumentManager} this
31495          * @param {Object} formData
31496          */
31497         "prepare" : true,
31498         /**
31499          * @event remove
31500          * Fire when remove the file
31501          * @param {Roo.bootstrap.DocumentManager} this
31502          * @param {Object} file
31503          */
31504         "remove" : true,
31505         /**
31506          * @event refresh
31507          * Fire after refresh the file
31508          * @param {Roo.bootstrap.DocumentManager} this
31509          */
31510         "refresh" : true,
31511         /**
31512          * @event click
31513          * Fire after click the image
31514          * @param {Roo.bootstrap.DocumentManager} this
31515          * @param {Object} file
31516          */
31517         "click" : true,
31518         /**
31519          * @event edit
31520          * Fire when upload a image and editable set to true
31521          * @param {Roo.bootstrap.DocumentManager} this
31522          * @param {Object} file
31523          */
31524         "edit" : true,
31525         /**
31526          * @event beforeselectfile
31527          * Fire before select file
31528          * @param {Roo.bootstrap.DocumentManager} this
31529          */
31530         "beforeselectfile" : true,
31531         /**
31532          * @event process
31533          * Fire before process file
31534          * @param {Roo.bootstrap.DocumentManager} this
31535          * @param {Object} file
31536          */
31537         "process" : true,
31538         /**
31539          * @event previewrendered
31540          * Fire when preview rendered
31541          * @param {Roo.bootstrap.DocumentManager} this
31542          * @param {Object} file
31543          */
31544         "previewrendered" : true,
31545         /**
31546          */
31547         "previewResize" : true
31548         
31549     });
31550 };
31551
31552 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31553     
31554     boxes : 0,
31555     inputName : '',
31556     thumbSize : 300,
31557     multiple : true,
31558     files : false,
31559     method : 'POST',
31560     url : '',
31561     paramName : 'imageUpload',
31562     toolTipName : 'filename',
31563     fieldLabel : '',
31564     labelWidth : 4,
31565     labelAlign : 'left',
31566     editable : true,
31567     delegates : false,
31568     xhr : false, 
31569     
31570     labellg : 0,
31571     labelmd : 0,
31572     labelsm : 0,
31573     labelxs : 0,
31574     
31575     getAutoCreate : function()
31576     {   
31577         var managerWidget = {
31578             tag : 'div',
31579             cls : 'roo-document-manager',
31580             cn : [
31581                 {
31582                     tag : 'input',
31583                     cls : 'roo-document-manager-selector',
31584                     type : 'file'
31585                 },
31586                 {
31587                     tag : 'div',
31588                     cls : 'roo-document-manager-uploader',
31589                     cn : [
31590                         {
31591                             tag : 'div',
31592                             cls : 'roo-document-manager-upload-btn',
31593                             html : '<i class="fa fa-plus"></i>'
31594                         }
31595                     ]
31596                     
31597                 }
31598             ]
31599         };
31600         
31601         var content = [
31602             {
31603                 tag : 'div',
31604                 cls : 'column col-md-12',
31605                 cn : managerWidget
31606             }
31607         ];
31608         
31609         if(this.fieldLabel.length){
31610             
31611             content = [
31612                 {
31613                     tag : 'div',
31614                     cls : 'column col-md-12',
31615                     html : this.fieldLabel
31616                 },
31617                 {
31618                     tag : 'div',
31619                     cls : 'column col-md-12',
31620                     cn : managerWidget
31621                 }
31622             ];
31623
31624             if(this.labelAlign == 'left'){
31625                 content = [
31626                     {
31627                         tag : 'div',
31628                         cls : 'column',
31629                         html : this.fieldLabel
31630                     },
31631                     {
31632                         tag : 'div',
31633                         cls : 'column',
31634                         cn : managerWidget
31635                     }
31636                 ];
31637                 
31638                 if(this.labelWidth > 12){
31639                     content[0].style = "width: " + this.labelWidth + 'px';
31640                 }
31641
31642                 if(this.labelWidth < 13 && this.labelmd == 0){
31643                     this.labelmd = this.labelWidth;
31644                 }
31645
31646                 if(this.labellg > 0){
31647                     content[0].cls += ' col-lg-' + this.labellg;
31648                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31649                 }
31650
31651                 if(this.labelmd > 0){
31652                     content[0].cls += ' col-md-' + this.labelmd;
31653                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31654                 }
31655
31656                 if(this.labelsm > 0){
31657                     content[0].cls += ' col-sm-' + this.labelsm;
31658                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31659                 }
31660
31661                 if(this.labelxs > 0){
31662                     content[0].cls += ' col-xs-' + this.labelxs;
31663                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31664                 }
31665                 
31666             }
31667         }
31668         
31669         var cfg = {
31670             tag : 'div',
31671             cls : 'row clearfix',
31672             cn : content
31673         };
31674         
31675         return cfg;
31676         
31677     },
31678     
31679     initEvents : function()
31680     {
31681         this.managerEl = this.el.select('.roo-document-manager', true).first();
31682         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31683         
31684         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31685         this.selectorEl.hide();
31686         
31687         if(this.multiple){
31688             this.selectorEl.attr('multiple', 'multiple');
31689         }
31690         
31691         this.selectorEl.on('change', this.onFileSelected, this);
31692         
31693         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31694         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31695         
31696         this.uploader.on('click', this.onUploaderClick, this);
31697         
31698         this.renderProgressDialog();
31699         
31700         var _this = this;
31701         
31702         window.addEventListener("resize", function() { _this.refresh(); } );
31703         
31704         this.fireEvent('initial', this);
31705     },
31706     
31707     renderProgressDialog : function()
31708     {
31709         var _this = this;
31710         
31711         this.progressDialog = new Roo.bootstrap.Modal({
31712             cls : 'roo-document-manager-progress-dialog',
31713             allow_close : false,
31714             animate : false,
31715             title : '',
31716             buttons : [
31717                 {
31718                     name  :'cancel',
31719                     weight : 'danger',
31720                     html : 'Cancel'
31721                 }
31722             ], 
31723             listeners : { 
31724                 btnclick : function() {
31725                     _this.uploadCancel();
31726                     this.hide();
31727                 }
31728             }
31729         });
31730          
31731         this.progressDialog.render(Roo.get(document.body));
31732          
31733         this.progress = new Roo.bootstrap.Progress({
31734             cls : 'roo-document-manager-progress',
31735             active : true,
31736             striped : true
31737         });
31738         
31739         this.progress.render(this.progressDialog.getChildContainer());
31740         
31741         this.progressBar = new Roo.bootstrap.ProgressBar({
31742             cls : 'roo-document-manager-progress-bar',
31743             aria_valuenow : 0,
31744             aria_valuemin : 0,
31745             aria_valuemax : 12,
31746             panel : 'success'
31747         });
31748         
31749         this.progressBar.render(this.progress.getChildContainer());
31750     },
31751     
31752     onUploaderClick : function(e)
31753     {
31754         e.preventDefault();
31755      
31756         if(this.fireEvent('beforeselectfile', this) != false){
31757             this.selectorEl.dom.click();
31758         }
31759         
31760     },
31761     
31762     onFileSelected : function(e)
31763     {
31764         e.preventDefault();
31765         
31766         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31767             return;
31768         }
31769         
31770         Roo.each(this.selectorEl.dom.files, function(file){
31771             if(this.fireEvent('inspect', this, file) != false){
31772                 this.files.push(file);
31773             }
31774         }, this);
31775         
31776         this.queue();
31777         
31778     },
31779     
31780     queue : function()
31781     {
31782         this.selectorEl.dom.value = '';
31783         
31784         if(!this.files || !this.files.length){
31785             return;
31786         }
31787         
31788         if(this.boxes > 0 && this.files.length > this.boxes){
31789             this.files = this.files.slice(0, this.boxes);
31790         }
31791         
31792         this.uploader.show();
31793         
31794         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31795             this.uploader.hide();
31796         }
31797         
31798         var _this = this;
31799         
31800         var files = [];
31801         
31802         var docs = [];
31803         
31804         Roo.each(this.files, function(file){
31805             
31806             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31807                 var f = this.renderPreview(file);
31808                 files.push(f);
31809                 return;
31810             }
31811             
31812             if(file.type.indexOf('image') != -1){
31813                 this.delegates.push(
31814                     (function(){
31815                         _this.process(file);
31816                     }).createDelegate(this)
31817                 );
31818         
31819                 return;
31820             }
31821             
31822             docs.push(
31823                 (function(){
31824                     _this.process(file);
31825                 }).createDelegate(this)
31826             );
31827             
31828         }, this);
31829         
31830         this.files = files;
31831         
31832         this.delegates = this.delegates.concat(docs);
31833         
31834         if(!this.delegates.length){
31835             this.refresh();
31836             return;
31837         }
31838         
31839         this.progressBar.aria_valuemax = this.delegates.length;
31840         
31841         this.arrange();
31842         
31843         return;
31844     },
31845     
31846     arrange : function()
31847     {
31848         if(!this.delegates.length){
31849             this.progressDialog.hide();
31850             this.refresh();
31851             return;
31852         }
31853         
31854         var delegate = this.delegates.shift();
31855         
31856         this.progressDialog.show();
31857         
31858         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31859         
31860         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31861         
31862         delegate();
31863     },
31864     
31865     refresh : function()
31866     {
31867         this.uploader.show();
31868         
31869         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31870             this.uploader.hide();
31871         }
31872         
31873         Roo.isTouch ? this.closable(false) : this.closable(true);
31874         
31875         this.fireEvent('refresh', this);
31876     },
31877     
31878     onRemove : function(e, el, o)
31879     {
31880         e.preventDefault();
31881         
31882         this.fireEvent('remove', this, o);
31883         
31884     },
31885     
31886     remove : function(o)
31887     {
31888         var files = [];
31889         
31890         Roo.each(this.files, function(file){
31891             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31892                 files.push(file);
31893                 return;
31894             }
31895
31896             o.target.remove();
31897
31898         }, this);
31899         
31900         this.files = files;
31901         
31902         this.refresh();
31903     },
31904     
31905     clear : function()
31906     {
31907         Roo.each(this.files, function(file){
31908             if(!file.target){
31909                 return;
31910             }
31911             
31912             file.target.remove();
31913
31914         }, this);
31915         
31916         this.files = [];
31917         
31918         this.refresh();
31919     },
31920     
31921     onClick : function(e, el, o)
31922     {
31923         e.preventDefault();
31924         
31925         this.fireEvent('click', this, o);
31926         
31927     },
31928     
31929     closable : function(closable)
31930     {
31931         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31932             
31933             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31934             
31935             if(closable){
31936                 el.show();
31937                 return;
31938             }
31939             
31940             el.hide();
31941             
31942         }, this);
31943     },
31944     
31945     xhrOnLoad : function(xhr)
31946     {
31947         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31948             el.remove();
31949         }, this);
31950         
31951         if (xhr.readyState !== 4) {
31952             this.arrange();
31953             this.fireEvent('exception', this, xhr);
31954             return;
31955         }
31956
31957         var response = Roo.decode(xhr.responseText);
31958         
31959         if(!response.success){
31960             this.arrange();
31961             this.fireEvent('exception', this, xhr);
31962             return;
31963         }
31964         
31965         var file = this.renderPreview(response.data);
31966         
31967         this.files.push(file);
31968         
31969         this.arrange();
31970         
31971         this.fireEvent('afterupload', this, xhr);
31972         
31973     },
31974     
31975     xhrOnError : function(xhr)
31976     {
31977         Roo.log('xhr on error');
31978         
31979         var response = Roo.decode(xhr.responseText);
31980           
31981         Roo.log(response);
31982         
31983         this.arrange();
31984     },
31985     
31986     process : function(file)
31987     {
31988         if(this.fireEvent('process', this, file) !== false){
31989             if(this.editable && file.type.indexOf('image') != -1){
31990                 this.fireEvent('edit', this, file);
31991                 return;
31992             }
31993
31994             this.uploadStart(file, false);
31995
31996             return;
31997         }
31998         
31999     },
32000     
32001     uploadStart : function(file, crop)
32002     {
32003         this.xhr = new XMLHttpRequest();
32004         
32005         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32006             this.arrange();
32007             return;
32008         }
32009         
32010         file.xhr = this.xhr;
32011             
32012         this.managerEl.createChild({
32013             tag : 'div',
32014             cls : 'roo-document-manager-loading',
32015             cn : [
32016                 {
32017                     tag : 'div',
32018                     tooltip : file.name,
32019                     cls : 'roo-document-manager-thumb',
32020                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32021                 }
32022             ]
32023
32024         });
32025
32026         this.xhr.open(this.method, this.url, true);
32027         
32028         var headers = {
32029             "Accept": "application/json",
32030             "Cache-Control": "no-cache",
32031             "X-Requested-With": "XMLHttpRequest"
32032         };
32033         
32034         for (var headerName in headers) {
32035             var headerValue = headers[headerName];
32036             if (headerValue) {
32037                 this.xhr.setRequestHeader(headerName, headerValue);
32038             }
32039         }
32040         
32041         var _this = this;
32042         
32043         this.xhr.onload = function()
32044         {
32045             _this.xhrOnLoad(_this.xhr);
32046         }
32047         
32048         this.xhr.onerror = function()
32049         {
32050             _this.xhrOnError(_this.xhr);
32051         }
32052         
32053         var formData = new FormData();
32054
32055         formData.append('returnHTML', 'NO');
32056         
32057         if(crop){
32058             formData.append('crop', crop);
32059         }
32060         
32061         formData.append(this.paramName, file, file.name);
32062         
32063         var options = {
32064             file : file, 
32065             manually : false
32066         };
32067         
32068         if(this.fireEvent('prepare', this, formData, options) != false){
32069             
32070             if(options.manually){
32071                 return;
32072             }
32073             
32074             this.xhr.send(formData);
32075             return;
32076         };
32077         
32078         this.uploadCancel();
32079     },
32080     
32081     uploadCancel : function()
32082     {
32083         if (this.xhr) {
32084             this.xhr.abort();
32085         }
32086         
32087         this.delegates = [];
32088         
32089         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32090             el.remove();
32091         }, this);
32092         
32093         this.arrange();
32094     },
32095     
32096     renderPreview : function(file)
32097     {
32098         if(typeof(file.target) != 'undefined' && file.target){
32099             return file;
32100         }
32101         
32102         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32103         
32104         var previewEl = this.managerEl.createChild({
32105             tag : 'div',
32106             cls : 'roo-document-manager-preview',
32107             cn : [
32108                 {
32109                     tag : 'div',
32110                     tooltip : file[this.toolTipName],
32111                     cls : 'roo-document-manager-thumb',
32112                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32113                 },
32114                 {
32115                     tag : 'button',
32116                     cls : 'close',
32117                     html : '<i class="fa fa-times-circle"></i>'
32118                 }
32119             ]
32120         });
32121
32122         var close = previewEl.select('button.close', true).first();
32123
32124         close.on('click', this.onRemove, this, file);
32125
32126         file.target = previewEl;
32127
32128         var image = previewEl.select('img', true).first();
32129         
32130         var _this = this;
32131         
32132         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32133         
32134         image.on('click', this.onClick, this, file);
32135         
32136         this.fireEvent('previewrendered', this, file);
32137         
32138         return file;
32139         
32140     },
32141     
32142     onPreviewLoad : function(file, image)
32143     {
32144         if(typeof(file.target) == 'undefined' || !file.target){
32145             return;
32146         }
32147         
32148         var width = image.dom.naturalWidth || image.dom.width;
32149         var height = image.dom.naturalHeight || image.dom.height;
32150         
32151         if(!this.previewResize) {
32152             return;
32153         }
32154         
32155         if(width > height){
32156             file.target.addClass('wide');
32157             return;
32158         }
32159         
32160         file.target.addClass('tall');
32161         return;
32162         
32163     },
32164     
32165     uploadFromSource : function(file, crop)
32166     {
32167         this.xhr = new XMLHttpRequest();
32168         
32169         this.managerEl.createChild({
32170             tag : 'div',
32171             cls : 'roo-document-manager-loading',
32172             cn : [
32173                 {
32174                     tag : 'div',
32175                     tooltip : file.name,
32176                     cls : 'roo-document-manager-thumb',
32177                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32178                 }
32179             ]
32180
32181         });
32182
32183         this.xhr.open(this.method, this.url, true);
32184         
32185         var headers = {
32186             "Accept": "application/json",
32187             "Cache-Control": "no-cache",
32188             "X-Requested-With": "XMLHttpRequest"
32189         };
32190         
32191         for (var headerName in headers) {
32192             var headerValue = headers[headerName];
32193             if (headerValue) {
32194                 this.xhr.setRequestHeader(headerName, headerValue);
32195             }
32196         }
32197         
32198         var _this = this;
32199         
32200         this.xhr.onload = function()
32201         {
32202             _this.xhrOnLoad(_this.xhr);
32203         }
32204         
32205         this.xhr.onerror = function()
32206         {
32207             _this.xhrOnError(_this.xhr);
32208         }
32209         
32210         var formData = new FormData();
32211
32212         formData.append('returnHTML', 'NO');
32213         
32214         formData.append('crop', crop);
32215         
32216         if(typeof(file.filename) != 'undefined'){
32217             formData.append('filename', file.filename);
32218         }
32219         
32220         if(typeof(file.mimetype) != 'undefined'){
32221             formData.append('mimetype', file.mimetype);
32222         }
32223         
32224         Roo.log(formData);
32225         
32226         if(this.fireEvent('prepare', this, formData) != false){
32227             this.xhr.send(formData);
32228         };
32229     }
32230 });
32231
32232 /*
32233 * Licence: LGPL
32234 */
32235
32236 /**
32237  * @class Roo.bootstrap.DocumentViewer
32238  * @extends Roo.bootstrap.Component
32239  * Bootstrap DocumentViewer class
32240  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32241  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32242  * 
32243  * @constructor
32244  * Create a new DocumentViewer
32245  * @param {Object} config The config object
32246  */
32247
32248 Roo.bootstrap.DocumentViewer = function(config){
32249     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32250     
32251     this.addEvents({
32252         /**
32253          * @event initial
32254          * Fire after initEvent
32255          * @param {Roo.bootstrap.DocumentViewer} this
32256          */
32257         "initial" : true,
32258         /**
32259          * @event click
32260          * Fire after click
32261          * @param {Roo.bootstrap.DocumentViewer} this
32262          */
32263         "click" : true,
32264         /**
32265          * @event download
32266          * Fire after download button
32267          * @param {Roo.bootstrap.DocumentViewer} this
32268          */
32269         "download" : true,
32270         /**
32271          * @event trash
32272          * Fire after trash button
32273          * @param {Roo.bootstrap.DocumentViewer} this
32274          */
32275         "trash" : true
32276         
32277     });
32278 };
32279
32280 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32281     
32282     showDownload : true,
32283     
32284     showTrash : true,
32285     
32286     getAutoCreate : function()
32287     {
32288         var cfg = {
32289             tag : 'div',
32290             cls : 'roo-document-viewer',
32291             cn : [
32292                 {
32293                     tag : 'div',
32294                     cls : 'roo-document-viewer-body',
32295                     cn : [
32296                         {
32297                             tag : 'div',
32298                             cls : 'roo-document-viewer-thumb',
32299                             cn : [
32300                                 {
32301                                     tag : 'img',
32302                                     cls : 'roo-document-viewer-image'
32303                                 }
32304                             ]
32305                         }
32306                     ]
32307                 },
32308                 {
32309                     tag : 'div',
32310                     cls : 'roo-document-viewer-footer',
32311                     cn : {
32312                         tag : 'div',
32313                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32314                         cn : [
32315                             {
32316                                 tag : 'div',
32317                                 cls : 'btn-group roo-document-viewer-download',
32318                                 cn : [
32319                                     {
32320                                         tag : 'button',
32321                                         cls : 'btn btn-default',
32322                                         html : '<i class="fa fa-download"></i>'
32323                                     }
32324                                 ]
32325                             },
32326                             {
32327                                 tag : 'div',
32328                                 cls : 'btn-group roo-document-viewer-trash',
32329                                 cn : [
32330                                     {
32331                                         tag : 'button',
32332                                         cls : 'btn btn-default',
32333                                         html : '<i class="fa fa-trash"></i>'
32334                                     }
32335                                 ]
32336                             }
32337                         ]
32338                     }
32339                 }
32340             ]
32341         };
32342         
32343         return cfg;
32344     },
32345     
32346     initEvents : function()
32347     {
32348         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32349         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32350         
32351         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32352         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32353         
32354         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32355         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32356         
32357         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32358         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32359         
32360         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32361         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32362         
32363         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32364         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32365         
32366         this.bodyEl.on('click', this.onClick, this);
32367         this.downloadBtn.on('click', this.onDownload, this);
32368         this.trashBtn.on('click', this.onTrash, this);
32369         
32370         this.downloadBtn.hide();
32371         this.trashBtn.hide();
32372         
32373         if(this.showDownload){
32374             this.downloadBtn.show();
32375         }
32376         
32377         if(this.showTrash){
32378             this.trashBtn.show();
32379         }
32380         
32381         if(!this.showDownload && !this.showTrash) {
32382             this.footerEl.hide();
32383         }
32384         
32385     },
32386     
32387     initial : function()
32388     {
32389         this.fireEvent('initial', this);
32390         
32391     },
32392     
32393     onClick : function(e)
32394     {
32395         e.preventDefault();
32396         
32397         this.fireEvent('click', this);
32398     },
32399     
32400     onDownload : function(e)
32401     {
32402         e.preventDefault();
32403         
32404         this.fireEvent('download', this);
32405     },
32406     
32407     onTrash : function(e)
32408     {
32409         e.preventDefault();
32410         
32411         this.fireEvent('trash', this);
32412     }
32413     
32414 });
32415 /*
32416  * - LGPL
32417  *
32418  * nav progress bar
32419  * 
32420  */
32421
32422 /**
32423  * @class Roo.bootstrap.NavProgressBar
32424  * @extends Roo.bootstrap.Component
32425  * Bootstrap NavProgressBar class
32426  * 
32427  * @constructor
32428  * Create a new nav progress bar
32429  * @param {Object} config The config object
32430  */
32431
32432 Roo.bootstrap.NavProgressBar = function(config){
32433     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32434
32435     this.bullets = this.bullets || [];
32436    
32437 //    Roo.bootstrap.NavProgressBar.register(this);
32438      this.addEvents({
32439         /**
32440              * @event changed
32441              * Fires when the active item changes
32442              * @param {Roo.bootstrap.NavProgressBar} this
32443              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32444              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32445          */
32446         'changed': true
32447      });
32448     
32449 };
32450
32451 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32452     
32453     bullets : [],
32454     barItems : [],
32455     
32456     getAutoCreate : function()
32457     {
32458         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32459         
32460         cfg = {
32461             tag : 'div',
32462             cls : 'roo-navigation-bar-group',
32463             cn : [
32464                 {
32465                     tag : 'div',
32466                     cls : 'roo-navigation-top-bar'
32467                 },
32468                 {
32469                     tag : 'div',
32470                     cls : 'roo-navigation-bullets-bar',
32471                     cn : [
32472                         {
32473                             tag : 'ul',
32474                             cls : 'roo-navigation-bar'
32475                         }
32476                     ]
32477                 },
32478                 
32479                 {
32480                     tag : 'div',
32481                     cls : 'roo-navigation-bottom-bar'
32482                 }
32483             ]
32484             
32485         };
32486         
32487         return cfg;
32488         
32489     },
32490     
32491     initEvents: function() 
32492     {
32493         
32494     },
32495     
32496     onRender : function(ct, position) 
32497     {
32498         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32499         
32500         if(this.bullets.length){
32501             Roo.each(this.bullets, function(b){
32502                this.addItem(b);
32503             }, this);
32504         }
32505         
32506         this.format();
32507         
32508     },
32509     
32510     addItem : function(cfg)
32511     {
32512         var item = new Roo.bootstrap.NavProgressItem(cfg);
32513         
32514         item.parentId = this.id;
32515         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32516         
32517         if(cfg.html){
32518             var top = new Roo.bootstrap.Element({
32519                 tag : 'div',
32520                 cls : 'roo-navigation-bar-text'
32521             });
32522             
32523             var bottom = new Roo.bootstrap.Element({
32524                 tag : 'div',
32525                 cls : 'roo-navigation-bar-text'
32526             });
32527             
32528             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32529             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32530             
32531             var topText = new Roo.bootstrap.Element({
32532                 tag : 'span',
32533                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32534             });
32535             
32536             var bottomText = new Roo.bootstrap.Element({
32537                 tag : 'span',
32538                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32539             });
32540             
32541             topText.onRender(top.el, null);
32542             bottomText.onRender(bottom.el, null);
32543             
32544             item.topEl = top;
32545             item.bottomEl = bottom;
32546         }
32547         
32548         this.barItems.push(item);
32549         
32550         return item;
32551     },
32552     
32553     getActive : function()
32554     {
32555         var active = false;
32556         
32557         Roo.each(this.barItems, function(v){
32558             
32559             if (!v.isActive()) {
32560                 return;
32561             }
32562             
32563             active = v;
32564             return false;
32565             
32566         });
32567         
32568         return active;
32569     },
32570     
32571     setActiveItem : function(item)
32572     {
32573         var prev = false;
32574         
32575         Roo.each(this.barItems, function(v){
32576             if (v.rid == item.rid) {
32577                 return ;
32578             }
32579             
32580             if (v.isActive()) {
32581                 v.setActive(false);
32582                 prev = v;
32583             }
32584         });
32585
32586         item.setActive(true);
32587         
32588         this.fireEvent('changed', this, item, prev);
32589     },
32590     
32591     getBarItem: function(rid)
32592     {
32593         var ret = false;
32594         
32595         Roo.each(this.barItems, function(e) {
32596             if (e.rid != rid) {
32597                 return;
32598             }
32599             
32600             ret =  e;
32601             return false;
32602         });
32603         
32604         return ret;
32605     },
32606     
32607     indexOfItem : function(item)
32608     {
32609         var index = false;
32610         
32611         Roo.each(this.barItems, function(v, i){
32612             
32613             if (v.rid != item.rid) {
32614                 return;
32615             }
32616             
32617             index = i;
32618             return false
32619         });
32620         
32621         return index;
32622     },
32623     
32624     setActiveNext : function()
32625     {
32626         var i = this.indexOfItem(this.getActive());
32627         
32628         if (i > this.barItems.length) {
32629             return;
32630         }
32631         
32632         this.setActiveItem(this.barItems[i+1]);
32633     },
32634     
32635     setActivePrev : function()
32636     {
32637         var i = this.indexOfItem(this.getActive());
32638         
32639         if (i  < 1) {
32640             return;
32641         }
32642         
32643         this.setActiveItem(this.barItems[i-1]);
32644     },
32645     
32646     format : function()
32647     {
32648         if(!this.barItems.length){
32649             return;
32650         }
32651      
32652         var width = 100 / this.barItems.length;
32653         
32654         Roo.each(this.barItems, function(i){
32655             i.el.setStyle('width', width + '%');
32656             i.topEl.el.setStyle('width', width + '%');
32657             i.bottomEl.el.setStyle('width', width + '%');
32658         }, this);
32659         
32660     }
32661     
32662 });
32663 /*
32664  * - LGPL
32665  *
32666  * Nav Progress Item
32667  * 
32668  */
32669
32670 /**
32671  * @class Roo.bootstrap.NavProgressItem
32672  * @extends Roo.bootstrap.Component
32673  * Bootstrap NavProgressItem class
32674  * @cfg {String} rid the reference id
32675  * @cfg {Boolean} active (true|false) Is item active default false
32676  * @cfg {Boolean} disabled (true|false) Is item active default false
32677  * @cfg {String} html
32678  * @cfg {String} position (top|bottom) text position default bottom
32679  * @cfg {String} icon show icon instead of number
32680  * 
32681  * @constructor
32682  * Create a new NavProgressItem
32683  * @param {Object} config The config object
32684  */
32685 Roo.bootstrap.NavProgressItem = function(config){
32686     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32687     this.addEvents({
32688         // raw events
32689         /**
32690          * @event click
32691          * The raw click event for the entire grid.
32692          * @param {Roo.bootstrap.NavProgressItem} this
32693          * @param {Roo.EventObject} e
32694          */
32695         "click" : true
32696     });
32697    
32698 };
32699
32700 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32701     
32702     rid : '',
32703     active : false,
32704     disabled : false,
32705     html : '',
32706     position : 'bottom',
32707     icon : false,
32708     
32709     getAutoCreate : function()
32710     {
32711         var iconCls = 'roo-navigation-bar-item-icon';
32712         
32713         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32714         
32715         var cfg = {
32716             tag: 'li',
32717             cls: 'roo-navigation-bar-item',
32718             cn : [
32719                 {
32720                     tag : 'i',
32721                     cls : iconCls
32722                 }
32723             ]
32724         };
32725         
32726         if(this.active){
32727             cfg.cls += ' active';
32728         }
32729         if(this.disabled){
32730             cfg.cls += ' disabled';
32731         }
32732         
32733         return cfg;
32734     },
32735     
32736     disable : function()
32737     {
32738         this.setDisabled(true);
32739     },
32740     
32741     enable : function()
32742     {
32743         this.setDisabled(false);
32744     },
32745     
32746     initEvents: function() 
32747     {
32748         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32749         
32750         this.iconEl.on('click', this.onClick, this);
32751     },
32752     
32753     onClick : function(e)
32754     {
32755         e.preventDefault();
32756         
32757         if(this.disabled){
32758             return;
32759         }
32760         
32761         if(this.fireEvent('click', this, e) === false){
32762             return;
32763         };
32764         
32765         this.parent().setActiveItem(this);
32766     },
32767     
32768     isActive: function () 
32769     {
32770         return this.active;
32771     },
32772     
32773     setActive : function(state)
32774     {
32775         if(this.active == state){
32776             return;
32777         }
32778         
32779         this.active = state;
32780         
32781         if (state) {
32782             this.el.addClass('active');
32783             return;
32784         }
32785         
32786         this.el.removeClass('active');
32787         
32788         return;
32789     },
32790     
32791     setDisabled : function(state)
32792     {
32793         if(this.disabled == state){
32794             return;
32795         }
32796         
32797         this.disabled = state;
32798         
32799         if (state) {
32800             this.el.addClass('disabled');
32801             return;
32802         }
32803         
32804         this.el.removeClass('disabled');
32805     },
32806     
32807     tooltipEl : function()
32808     {
32809         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32810     }
32811 });
32812  
32813
32814  /*
32815  * - LGPL
32816  *
32817  * FieldLabel
32818  * 
32819  */
32820
32821 /**
32822  * @class Roo.bootstrap.FieldLabel
32823  * @extends Roo.bootstrap.Component
32824  * Bootstrap FieldLabel class
32825  * @cfg {String} html contents of the element
32826  * @cfg {String} tag tag of the element default label
32827  * @cfg {String} cls class of the element
32828  * @cfg {String} target label target 
32829  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32830  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32831  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32832  * @cfg {String} iconTooltip default "This field is required"
32833  * @cfg {String} indicatorpos (left|right) default left
32834  * 
32835  * @constructor
32836  * Create a new FieldLabel
32837  * @param {Object} config The config object
32838  */
32839
32840 Roo.bootstrap.FieldLabel = function(config){
32841     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32842     
32843     this.addEvents({
32844             /**
32845              * @event invalid
32846              * Fires after the field has been marked as invalid.
32847              * @param {Roo.form.FieldLabel} this
32848              * @param {String} msg The validation message
32849              */
32850             invalid : true,
32851             /**
32852              * @event valid
32853              * Fires after the field has been validated with no errors.
32854              * @param {Roo.form.FieldLabel} this
32855              */
32856             valid : true
32857         });
32858 };
32859
32860 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32861     
32862     tag: 'label',
32863     cls: '',
32864     html: '',
32865     target: '',
32866     allowBlank : true,
32867     invalidClass : 'has-warning',
32868     validClass : 'has-success',
32869     iconTooltip : 'This field is required',
32870     indicatorpos : 'left',
32871     
32872     getAutoCreate : function(){
32873         
32874         var cls = "";
32875         if (!this.allowBlank) {
32876             cls  = "visible";
32877         }
32878         
32879         var cfg = {
32880             tag : this.tag,
32881             cls : 'roo-bootstrap-field-label ' + this.cls,
32882             for : this.target,
32883             cn : [
32884                 {
32885                     tag : 'i',
32886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32887                     tooltip : this.iconTooltip
32888                 },
32889                 {
32890                     tag : 'span',
32891                     html : this.html
32892                 }
32893             ] 
32894         };
32895         
32896         if(this.indicatorpos == 'right'){
32897             var cfg = {
32898                 tag : this.tag,
32899                 cls : 'roo-bootstrap-field-label ' + this.cls,
32900                 for : this.target,
32901                 cn : [
32902                     {
32903                         tag : 'span',
32904                         html : this.html
32905                     },
32906                     {
32907                         tag : 'i',
32908                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32909                         tooltip : this.iconTooltip
32910                     }
32911                 ] 
32912             };
32913         }
32914         
32915         return cfg;
32916     },
32917     
32918     initEvents: function() 
32919     {
32920         Roo.bootstrap.Element.superclass.initEvents.call(this);
32921         
32922         this.indicator = this.indicatorEl();
32923         
32924         if(this.indicator){
32925             this.indicator.removeClass('visible');
32926             this.indicator.addClass('invisible');
32927         }
32928         
32929         Roo.bootstrap.FieldLabel.register(this);
32930     },
32931     
32932     indicatorEl : function()
32933     {
32934         var indicator = this.el.select('i.roo-required-indicator',true).first();
32935         
32936         if(!indicator){
32937             return false;
32938         }
32939         
32940         return indicator;
32941         
32942     },
32943     
32944     /**
32945      * Mark this field as valid
32946      */
32947     markValid : function()
32948     {
32949         if(this.indicator){
32950             this.indicator.removeClass('visible');
32951             this.indicator.addClass('invisible');
32952         }
32953         if (Roo.bootstrap.version == 3) {
32954             this.el.removeClass(this.invalidClass);
32955             this.el.addClass(this.validClass);
32956         } else {
32957             this.el.removeClass('is-invalid');
32958             this.el.addClass('is-valid');
32959         }
32960         
32961         
32962         this.fireEvent('valid', this);
32963     },
32964     
32965     /**
32966      * Mark this field as invalid
32967      * @param {String} msg The validation message
32968      */
32969     markInvalid : function(msg)
32970     {
32971         if(this.indicator){
32972             this.indicator.removeClass('invisible');
32973             this.indicator.addClass('visible');
32974         }
32975           if (Roo.bootstrap.version == 3) {
32976             this.el.removeClass(this.validClass);
32977             this.el.addClass(this.invalidClass);
32978         } else {
32979             this.el.removeClass('is-valid');
32980             this.el.addClass('is-invalid');
32981         }
32982         
32983         
32984         this.fireEvent('invalid', this, msg);
32985     }
32986     
32987    
32988 });
32989
32990 Roo.apply(Roo.bootstrap.FieldLabel, {
32991     
32992     groups: {},
32993     
32994      /**
32995     * register a FieldLabel Group
32996     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32997     */
32998     register : function(label)
32999     {
33000         if(this.groups.hasOwnProperty(label.target)){
33001             return;
33002         }
33003      
33004         this.groups[label.target] = label;
33005         
33006     },
33007     /**
33008     * fetch a FieldLabel Group based on the target
33009     * @param {string} target
33010     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33011     */
33012     get: function(target) {
33013         if (typeof(this.groups[target]) == 'undefined') {
33014             return false;
33015         }
33016         
33017         return this.groups[target] ;
33018     }
33019 });
33020
33021  
33022
33023  /*
33024  * - LGPL
33025  *
33026  * page DateSplitField.
33027  * 
33028  */
33029
33030
33031 /**
33032  * @class Roo.bootstrap.DateSplitField
33033  * @extends Roo.bootstrap.Component
33034  * Bootstrap DateSplitField class
33035  * @cfg {string} fieldLabel - the label associated
33036  * @cfg {Number} labelWidth set the width of label (0-12)
33037  * @cfg {String} labelAlign (top|left)
33038  * @cfg {Boolean} dayAllowBlank (true|false) default false
33039  * @cfg {Boolean} monthAllowBlank (true|false) default false
33040  * @cfg {Boolean} yearAllowBlank (true|false) default false
33041  * @cfg {string} dayPlaceholder 
33042  * @cfg {string} monthPlaceholder
33043  * @cfg {string} yearPlaceholder
33044  * @cfg {string} dayFormat default 'd'
33045  * @cfg {string} monthFormat default 'm'
33046  * @cfg {string} yearFormat default 'Y'
33047  * @cfg {Number} labellg set the width of label (1-12)
33048  * @cfg {Number} labelmd set the width of label (1-12)
33049  * @cfg {Number} labelsm set the width of label (1-12)
33050  * @cfg {Number} labelxs set the width of label (1-12)
33051
33052  *     
33053  * @constructor
33054  * Create a new DateSplitField
33055  * @param {Object} config The config object
33056  */
33057
33058 Roo.bootstrap.DateSplitField = function(config){
33059     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33060     
33061     this.addEvents({
33062         // raw events
33063          /**
33064          * @event years
33065          * getting the data of years
33066          * @param {Roo.bootstrap.DateSplitField} this
33067          * @param {Object} years
33068          */
33069         "years" : true,
33070         /**
33071          * @event days
33072          * getting the data of days
33073          * @param {Roo.bootstrap.DateSplitField} this
33074          * @param {Object} days
33075          */
33076         "days" : true,
33077         /**
33078          * @event invalid
33079          * Fires after the field has been marked as invalid.
33080          * @param {Roo.form.Field} this
33081          * @param {String} msg The validation message
33082          */
33083         invalid : true,
33084        /**
33085          * @event valid
33086          * Fires after the field has been validated with no errors.
33087          * @param {Roo.form.Field} this
33088          */
33089         valid : true
33090     });
33091 };
33092
33093 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33094     
33095     fieldLabel : '',
33096     labelAlign : 'top',
33097     labelWidth : 3,
33098     dayAllowBlank : false,
33099     monthAllowBlank : false,
33100     yearAllowBlank : false,
33101     dayPlaceholder : '',
33102     monthPlaceholder : '',
33103     yearPlaceholder : '',
33104     dayFormat : 'd',
33105     monthFormat : 'm',
33106     yearFormat : 'Y',
33107     isFormField : true,
33108     labellg : 0,
33109     labelmd : 0,
33110     labelsm : 0,
33111     labelxs : 0,
33112     
33113     getAutoCreate : function()
33114     {
33115         var cfg = {
33116             tag : 'div',
33117             cls : 'row roo-date-split-field-group',
33118             cn : [
33119                 {
33120                     tag : 'input',
33121                     type : 'hidden',
33122                     cls : 'form-hidden-field roo-date-split-field-group-value',
33123                     name : this.name
33124                 }
33125             ]
33126         };
33127         
33128         var labelCls = 'col-md-12';
33129         var contentCls = 'col-md-4';
33130         
33131         if(this.fieldLabel){
33132             
33133             var label = {
33134                 tag : 'div',
33135                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33136                 cn : [
33137                     {
33138                         tag : 'label',
33139                         html : this.fieldLabel
33140                     }
33141                 ]
33142             };
33143             
33144             if(this.labelAlign == 'left'){
33145             
33146                 if(this.labelWidth > 12){
33147                     label.style = "width: " + this.labelWidth + 'px';
33148                 }
33149
33150                 if(this.labelWidth < 13 && this.labelmd == 0){
33151                     this.labelmd = this.labelWidth;
33152                 }
33153
33154                 if(this.labellg > 0){
33155                     labelCls = ' col-lg-' + this.labellg;
33156                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33157                 }
33158
33159                 if(this.labelmd > 0){
33160                     labelCls = ' col-md-' + this.labelmd;
33161                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33162                 }
33163
33164                 if(this.labelsm > 0){
33165                     labelCls = ' col-sm-' + this.labelsm;
33166                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33167                 }
33168
33169                 if(this.labelxs > 0){
33170                     labelCls = ' col-xs-' + this.labelxs;
33171                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33172                 }
33173             }
33174             
33175             label.cls += ' ' + labelCls;
33176             
33177             cfg.cn.push(label);
33178         }
33179         
33180         Roo.each(['day', 'month', 'year'], function(t){
33181             cfg.cn.push({
33182                 tag : 'div',
33183                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33184             });
33185         }, this);
33186         
33187         return cfg;
33188     },
33189     
33190     inputEl: function ()
33191     {
33192         return this.el.select('.roo-date-split-field-group-value', true).first();
33193     },
33194     
33195     onRender : function(ct, position) 
33196     {
33197         var _this = this;
33198         
33199         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33200         
33201         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33202         
33203         this.dayField = new Roo.bootstrap.ComboBox({
33204             allowBlank : this.dayAllowBlank,
33205             alwaysQuery : true,
33206             displayField : 'value',
33207             editable : false,
33208             fieldLabel : '',
33209             forceSelection : true,
33210             mode : 'local',
33211             placeholder : this.dayPlaceholder,
33212             selectOnFocus : true,
33213             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33214             triggerAction : 'all',
33215             typeAhead : true,
33216             valueField : 'value',
33217             store : new Roo.data.SimpleStore({
33218                 data : (function() {    
33219                     var days = [];
33220                     _this.fireEvent('days', _this, days);
33221                     return days;
33222                 })(),
33223                 fields : [ 'value' ]
33224             }),
33225             listeners : {
33226                 select : function (_self, record, index)
33227                 {
33228                     _this.setValue(_this.getValue());
33229                 }
33230             }
33231         });
33232
33233         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33234         
33235         this.monthField = new Roo.bootstrap.MonthField({
33236             after : '<i class=\"fa fa-calendar\"></i>',
33237             allowBlank : this.monthAllowBlank,
33238             placeholder : this.monthPlaceholder,
33239             readOnly : true,
33240             listeners : {
33241                 render : function (_self)
33242                 {
33243                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33244                         e.preventDefault();
33245                         _self.focus();
33246                     });
33247                 },
33248                 select : function (_self, oldvalue, newvalue)
33249                 {
33250                     _this.setValue(_this.getValue());
33251                 }
33252             }
33253         });
33254         
33255         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33256         
33257         this.yearField = new Roo.bootstrap.ComboBox({
33258             allowBlank : this.yearAllowBlank,
33259             alwaysQuery : true,
33260             displayField : 'value',
33261             editable : false,
33262             fieldLabel : '',
33263             forceSelection : true,
33264             mode : 'local',
33265             placeholder : this.yearPlaceholder,
33266             selectOnFocus : true,
33267             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33268             triggerAction : 'all',
33269             typeAhead : true,
33270             valueField : 'value',
33271             store : new Roo.data.SimpleStore({
33272                 data : (function() {
33273                     var years = [];
33274                     _this.fireEvent('years', _this, years);
33275                     return years;
33276                 })(),
33277                 fields : [ 'value' ]
33278             }),
33279             listeners : {
33280                 select : function (_self, record, index)
33281                 {
33282                     _this.setValue(_this.getValue());
33283                 }
33284             }
33285         });
33286
33287         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33288     },
33289     
33290     setValue : function(v, format)
33291     {
33292         this.inputEl.dom.value = v;
33293         
33294         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33295         
33296         var d = Date.parseDate(v, f);
33297         
33298         if(!d){
33299             this.validate();
33300             return;
33301         }
33302         
33303         this.setDay(d.format(this.dayFormat));
33304         this.setMonth(d.format(this.monthFormat));
33305         this.setYear(d.format(this.yearFormat));
33306         
33307         this.validate();
33308         
33309         return;
33310     },
33311     
33312     setDay : function(v)
33313     {
33314         this.dayField.setValue(v);
33315         this.inputEl.dom.value = this.getValue();
33316         this.validate();
33317         return;
33318     },
33319     
33320     setMonth : function(v)
33321     {
33322         this.monthField.setValue(v, true);
33323         this.inputEl.dom.value = this.getValue();
33324         this.validate();
33325         return;
33326     },
33327     
33328     setYear : function(v)
33329     {
33330         this.yearField.setValue(v);
33331         this.inputEl.dom.value = this.getValue();
33332         this.validate();
33333         return;
33334     },
33335     
33336     getDay : function()
33337     {
33338         return this.dayField.getValue();
33339     },
33340     
33341     getMonth : function()
33342     {
33343         return this.monthField.getValue();
33344     },
33345     
33346     getYear : function()
33347     {
33348         return this.yearField.getValue();
33349     },
33350     
33351     getValue : function()
33352     {
33353         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33354         
33355         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33356         
33357         return date;
33358     },
33359     
33360     reset : function()
33361     {
33362         this.setDay('');
33363         this.setMonth('');
33364         this.setYear('');
33365         this.inputEl.dom.value = '';
33366         this.validate();
33367         return;
33368     },
33369     
33370     validate : function()
33371     {
33372         var d = this.dayField.validate();
33373         var m = this.monthField.validate();
33374         var y = this.yearField.validate();
33375         
33376         var valid = true;
33377         
33378         if(
33379                 (!this.dayAllowBlank && !d) ||
33380                 (!this.monthAllowBlank && !m) ||
33381                 (!this.yearAllowBlank && !y)
33382         ){
33383             valid = false;
33384         }
33385         
33386         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33387             return valid;
33388         }
33389         
33390         if(valid){
33391             this.markValid();
33392             return valid;
33393         }
33394         
33395         this.markInvalid();
33396         
33397         return valid;
33398     },
33399     
33400     markValid : function()
33401     {
33402         
33403         var label = this.el.select('label', true).first();
33404         var icon = this.el.select('i.fa-star', true).first();
33405
33406         if(label && icon){
33407             icon.remove();
33408         }
33409         
33410         this.fireEvent('valid', this);
33411     },
33412     
33413      /**
33414      * Mark this field as invalid
33415      * @param {String} msg The validation message
33416      */
33417     markInvalid : function(msg)
33418     {
33419         
33420         var label = this.el.select('label', true).first();
33421         var icon = this.el.select('i.fa-star', true).first();
33422
33423         if(label && !icon){
33424             this.el.select('.roo-date-split-field-label', true).createChild({
33425                 tag : 'i',
33426                 cls : 'text-danger fa fa-lg fa-star',
33427                 tooltip : 'This field is required',
33428                 style : 'margin-right:5px;'
33429             }, label, true);
33430         }
33431         
33432         this.fireEvent('invalid', this, msg);
33433     },
33434     
33435     clearInvalid : function()
33436     {
33437         var label = this.el.select('label', true).first();
33438         var icon = this.el.select('i.fa-star', true).first();
33439
33440         if(label && icon){
33441             icon.remove();
33442         }
33443         
33444         this.fireEvent('valid', this);
33445     },
33446     
33447     getName: function()
33448     {
33449         return this.name;
33450     }
33451     
33452 });
33453
33454  /**
33455  *
33456  * This is based on 
33457  * http://masonry.desandro.com
33458  *
33459  * The idea is to render all the bricks based on vertical width...
33460  *
33461  * The original code extends 'outlayer' - we might need to use that....
33462  * 
33463  */
33464
33465
33466 /**
33467  * @class Roo.bootstrap.LayoutMasonry
33468  * @extends Roo.bootstrap.Component
33469  * Bootstrap Layout Masonry class
33470  * 
33471  * @constructor
33472  * Create a new Element
33473  * @param {Object} config The config object
33474  */
33475
33476 Roo.bootstrap.LayoutMasonry = function(config){
33477     
33478     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33479     
33480     this.bricks = [];
33481     
33482     Roo.bootstrap.LayoutMasonry.register(this);
33483     
33484     this.addEvents({
33485         // raw events
33486         /**
33487          * @event layout
33488          * Fire after layout the items
33489          * @param {Roo.bootstrap.LayoutMasonry} this
33490          * @param {Roo.EventObject} e
33491          */
33492         "layout" : true
33493     });
33494     
33495 };
33496
33497 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33498     
33499     /**
33500      * @cfg {Boolean} isLayoutInstant = no animation?
33501      */   
33502     isLayoutInstant : false, // needed?
33503    
33504     /**
33505      * @cfg {Number} boxWidth  width of the columns
33506      */   
33507     boxWidth : 450,
33508     
33509       /**
33510      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33511      */   
33512     boxHeight : 0,
33513     
33514     /**
33515      * @cfg {Number} padWidth padding below box..
33516      */   
33517     padWidth : 10, 
33518     
33519     /**
33520      * @cfg {Number} gutter gutter width..
33521      */   
33522     gutter : 10,
33523     
33524      /**
33525      * @cfg {Number} maxCols maximum number of columns
33526      */   
33527     
33528     maxCols: 0,
33529     
33530     /**
33531      * @cfg {Boolean} isAutoInitial defalut true
33532      */   
33533     isAutoInitial : true, 
33534     
33535     containerWidth: 0,
33536     
33537     /**
33538      * @cfg {Boolean} isHorizontal defalut false
33539      */   
33540     isHorizontal : false, 
33541
33542     currentSize : null,
33543     
33544     tag: 'div',
33545     
33546     cls: '',
33547     
33548     bricks: null, //CompositeElement
33549     
33550     cols : 1,
33551     
33552     _isLayoutInited : false,
33553     
33554 //    isAlternative : false, // only use for vertical layout...
33555     
33556     /**
33557      * @cfg {Number} alternativePadWidth padding below box..
33558      */   
33559     alternativePadWidth : 50,
33560     
33561     selectedBrick : [],
33562     
33563     getAutoCreate : function(){
33564         
33565         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33566         
33567         var cfg = {
33568             tag: this.tag,
33569             cls: 'blog-masonary-wrapper ' + this.cls,
33570             cn : {
33571                 cls : 'mas-boxes masonary'
33572             }
33573         };
33574         
33575         return cfg;
33576     },
33577     
33578     getChildContainer: function( )
33579     {
33580         if (this.boxesEl) {
33581             return this.boxesEl;
33582         }
33583         
33584         this.boxesEl = this.el.select('.mas-boxes').first();
33585         
33586         return this.boxesEl;
33587     },
33588     
33589     
33590     initEvents : function()
33591     {
33592         var _this = this;
33593         
33594         if(this.isAutoInitial){
33595             Roo.log('hook children rendered');
33596             this.on('childrenrendered', function() {
33597                 Roo.log('children rendered');
33598                 _this.initial();
33599             } ,this);
33600         }
33601     },
33602     
33603     initial : function()
33604     {
33605         this.selectedBrick = [];
33606         
33607         this.currentSize = this.el.getBox(true);
33608         
33609         Roo.EventManager.onWindowResize(this.resize, this); 
33610
33611         if(!this.isAutoInitial){
33612             this.layout();
33613             return;
33614         }
33615         
33616         this.layout();
33617         
33618         return;
33619         //this.layout.defer(500,this);
33620         
33621     },
33622     
33623     resize : function()
33624     {
33625         var cs = this.el.getBox(true);
33626         
33627         if (
33628                 this.currentSize.width == cs.width && 
33629                 this.currentSize.x == cs.x && 
33630                 this.currentSize.height == cs.height && 
33631                 this.currentSize.y == cs.y 
33632         ) {
33633             Roo.log("no change in with or X or Y");
33634             return;
33635         }
33636         
33637         this.currentSize = cs;
33638         
33639         this.layout();
33640         
33641     },
33642     
33643     layout : function()
33644     {   
33645         this._resetLayout();
33646         
33647         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33648         
33649         this.layoutItems( isInstant );
33650       
33651         this._isLayoutInited = true;
33652         
33653         this.fireEvent('layout', this);
33654         
33655     },
33656     
33657     _resetLayout : function()
33658     {
33659         if(this.isHorizontal){
33660             this.horizontalMeasureColumns();
33661             return;
33662         }
33663         
33664         this.verticalMeasureColumns();
33665         
33666     },
33667     
33668     verticalMeasureColumns : function()
33669     {
33670         this.getContainerWidth();
33671         
33672 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33673 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33674 //            return;
33675 //        }
33676         
33677         var boxWidth = this.boxWidth + this.padWidth;
33678         
33679         if(this.containerWidth < this.boxWidth){
33680             boxWidth = this.containerWidth
33681         }
33682         
33683         var containerWidth = this.containerWidth;
33684         
33685         var cols = Math.floor(containerWidth / boxWidth);
33686         
33687         this.cols = Math.max( cols, 1 );
33688         
33689         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33690         
33691         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33692         
33693         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33694         
33695         this.colWidth = boxWidth + avail - this.padWidth;
33696         
33697         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33698         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33699     },
33700     
33701     horizontalMeasureColumns : function()
33702     {
33703         this.getContainerWidth();
33704         
33705         var boxWidth = this.boxWidth;
33706         
33707         if(this.containerWidth < boxWidth){
33708             boxWidth = this.containerWidth;
33709         }
33710         
33711         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33712         
33713         this.el.setHeight(boxWidth);
33714         
33715     },
33716     
33717     getContainerWidth : function()
33718     {
33719         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33720     },
33721     
33722     layoutItems : function( isInstant )
33723     {
33724         Roo.log(this.bricks);
33725         
33726         var items = Roo.apply([], this.bricks);
33727         
33728         if(this.isHorizontal){
33729             this._horizontalLayoutItems( items , isInstant );
33730             return;
33731         }
33732         
33733 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33734 //            this._verticalAlternativeLayoutItems( items , isInstant );
33735 //            return;
33736 //        }
33737         
33738         this._verticalLayoutItems( items , isInstant );
33739         
33740     },
33741     
33742     _verticalLayoutItems : function ( items , isInstant)
33743     {
33744         if ( !items || !items.length ) {
33745             return;
33746         }
33747         
33748         var standard = [
33749             ['xs', 'xs', 'xs', 'tall'],
33750             ['xs', 'xs', 'tall'],
33751             ['xs', 'xs', 'sm'],
33752             ['xs', 'xs', 'xs'],
33753             ['xs', 'tall'],
33754             ['xs', 'sm'],
33755             ['xs', 'xs'],
33756             ['xs'],
33757             
33758             ['sm', 'xs', 'xs'],
33759             ['sm', 'xs'],
33760             ['sm'],
33761             
33762             ['tall', 'xs', 'xs', 'xs'],
33763             ['tall', 'xs', 'xs'],
33764             ['tall', 'xs'],
33765             ['tall']
33766             
33767         ];
33768         
33769         var queue = [];
33770         
33771         var boxes = [];
33772         
33773         var box = [];
33774         
33775         Roo.each(items, function(item, k){
33776             
33777             switch (item.size) {
33778                 // these layouts take up a full box,
33779                 case 'md' :
33780                 case 'md-left' :
33781                 case 'md-right' :
33782                 case 'wide' :
33783                     
33784                     if(box.length){
33785                         boxes.push(box);
33786                         box = [];
33787                     }
33788                     
33789                     boxes.push([item]);
33790                     
33791                     break;
33792                     
33793                 case 'xs' :
33794                 case 'sm' :
33795                 case 'tall' :
33796                     
33797                     box.push(item);
33798                     
33799                     break;
33800                 default :
33801                     break;
33802                     
33803             }
33804             
33805         }, this);
33806         
33807         if(box.length){
33808             boxes.push(box);
33809             box = [];
33810         }
33811         
33812         var filterPattern = function(box, length)
33813         {
33814             if(!box.length){
33815                 return;
33816             }
33817             
33818             var match = false;
33819             
33820             var pattern = box.slice(0, length);
33821             
33822             var format = [];
33823             
33824             Roo.each(pattern, function(i){
33825                 format.push(i.size);
33826             }, this);
33827             
33828             Roo.each(standard, function(s){
33829                 
33830                 if(String(s) != String(format)){
33831                     return;
33832                 }
33833                 
33834                 match = true;
33835                 return false;
33836                 
33837             }, this);
33838             
33839             if(!match && length == 1){
33840                 return;
33841             }
33842             
33843             if(!match){
33844                 filterPattern(box, length - 1);
33845                 return;
33846             }
33847                 
33848             queue.push(pattern);
33849
33850             box = box.slice(length, box.length);
33851
33852             filterPattern(box, 4);
33853
33854             return;
33855             
33856         }
33857         
33858         Roo.each(boxes, function(box, k){
33859             
33860             if(!box.length){
33861                 return;
33862             }
33863             
33864             if(box.length == 1){
33865                 queue.push(box);
33866                 return;
33867             }
33868             
33869             filterPattern(box, 4);
33870             
33871         }, this);
33872         
33873         this._processVerticalLayoutQueue( queue, isInstant );
33874         
33875     },
33876     
33877 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33878 //    {
33879 //        if ( !items || !items.length ) {
33880 //            return;
33881 //        }
33882 //
33883 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33884 //        
33885 //    },
33886     
33887     _horizontalLayoutItems : function ( items , isInstant)
33888     {
33889         if ( !items || !items.length || items.length < 3) {
33890             return;
33891         }
33892         
33893         items.reverse();
33894         
33895         var eItems = items.slice(0, 3);
33896         
33897         items = items.slice(3, items.length);
33898         
33899         var standard = [
33900             ['xs', 'xs', 'xs', 'wide'],
33901             ['xs', 'xs', 'wide'],
33902             ['xs', 'xs', 'sm'],
33903             ['xs', 'xs', 'xs'],
33904             ['xs', 'wide'],
33905             ['xs', 'sm'],
33906             ['xs', 'xs'],
33907             ['xs'],
33908             
33909             ['sm', 'xs', 'xs'],
33910             ['sm', 'xs'],
33911             ['sm'],
33912             
33913             ['wide', 'xs', 'xs', 'xs'],
33914             ['wide', 'xs', 'xs'],
33915             ['wide', 'xs'],
33916             ['wide'],
33917             
33918             ['wide-thin']
33919         ];
33920         
33921         var queue = [];
33922         
33923         var boxes = [];
33924         
33925         var box = [];
33926         
33927         Roo.each(items, function(item, k){
33928             
33929             switch (item.size) {
33930                 case 'md' :
33931                 case 'md-left' :
33932                 case 'md-right' :
33933                 case 'tall' :
33934                     
33935                     if(box.length){
33936                         boxes.push(box);
33937                         box = [];
33938                     }
33939                     
33940                     boxes.push([item]);
33941                     
33942                     break;
33943                     
33944                 case 'xs' :
33945                 case 'sm' :
33946                 case 'wide' :
33947                 case 'wide-thin' :
33948                     
33949                     box.push(item);
33950                     
33951                     break;
33952                 default :
33953                     break;
33954                     
33955             }
33956             
33957         }, this);
33958         
33959         if(box.length){
33960             boxes.push(box);
33961             box = [];
33962         }
33963         
33964         var filterPattern = function(box, length)
33965         {
33966             if(!box.length){
33967                 return;
33968             }
33969             
33970             var match = false;
33971             
33972             var pattern = box.slice(0, length);
33973             
33974             var format = [];
33975             
33976             Roo.each(pattern, function(i){
33977                 format.push(i.size);
33978             }, this);
33979             
33980             Roo.each(standard, function(s){
33981                 
33982                 if(String(s) != String(format)){
33983                     return;
33984                 }
33985                 
33986                 match = true;
33987                 return false;
33988                 
33989             }, this);
33990             
33991             if(!match && length == 1){
33992                 return;
33993             }
33994             
33995             if(!match){
33996                 filterPattern(box, length - 1);
33997                 return;
33998             }
33999                 
34000             queue.push(pattern);
34001
34002             box = box.slice(length, box.length);
34003
34004             filterPattern(box, 4);
34005
34006             return;
34007             
34008         }
34009         
34010         Roo.each(boxes, function(box, k){
34011             
34012             if(!box.length){
34013                 return;
34014             }
34015             
34016             if(box.length == 1){
34017                 queue.push(box);
34018                 return;
34019             }
34020             
34021             filterPattern(box, 4);
34022             
34023         }, this);
34024         
34025         
34026         var prune = [];
34027         
34028         var pos = this.el.getBox(true);
34029         
34030         var minX = pos.x;
34031         
34032         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34033         
34034         var hit_end = false;
34035         
34036         Roo.each(queue, function(box){
34037             
34038             if(hit_end){
34039                 
34040                 Roo.each(box, function(b){
34041                 
34042                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34043                     b.el.hide();
34044
34045                 }, this);
34046
34047                 return;
34048             }
34049             
34050             var mx = 0;
34051             
34052             Roo.each(box, function(b){
34053                 
34054                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34055                 b.el.show();
34056
34057                 mx = Math.max(mx, b.x);
34058                 
34059             }, this);
34060             
34061             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34062             
34063             if(maxX < minX){
34064                 
34065                 Roo.each(box, function(b){
34066                 
34067                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34068                     b.el.hide();
34069                     
34070                 }, this);
34071                 
34072                 hit_end = true;
34073                 
34074                 return;
34075             }
34076             
34077             prune.push(box);
34078             
34079         }, this);
34080         
34081         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34082     },
34083     
34084     /** Sets position of item in DOM
34085     * @param {Element} item
34086     * @param {Number} x - horizontal position
34087     * @param {Number} y - vertical position
34088     * @param {Boolean} isInstant - disables transitions
34089     */
34090     _processVerticalLayoutQueue : function( queue, isInstant )
34091     {
34092         var pos = this.el.getBox(true);
34093         var x = pos.x;
34094         var y = pos.y;
34095         var maxY = [];
34096         
34097         for (var i = 0; i < this.cols; i++){
34098             maxY[i] = pos.y;
34099         }
34100         
34101         Roo.each(queue, function(box, k){
34102             
34103             var col = k % this.cols;
34104             
34105             Roo.each(box, function(b,kk){
34106                 
34107                 b.el.position('absolute');
34108                 
34109                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34110                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34111                 
34112                 if(b.size == 'md-left' || b.size == 'md-right'){
34113                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34114                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34115                 }
34116                 
34117                 b.el.setWidth(width);
34118                 b.el.setHeight(height);
34119                 // iframe?
34120                 b.el.select('iframe',true).setSize(width,height);
34121                 
34122             }, this);
34123             
34124             for (var i = 0; i < this.cols; i++){
34125                 
34126                 if(maxY[i] < maxY[col]){
34127                     col = i;
34128                     continue;
34129                 }
34130                 
34131                 col = Math.min(col, i);
34132                 
34133             }
34134             
34135             x = pos.x + col * (this.colWidth + this.padWidth);
34136             
34137             y = maxY[col];
34138             
34139             var positions = [];
34140             
34141             switch (box.length){
34142                 case 1 :
34143                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34144                     break;
34145                 case 2 :
34146                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34147                     break;
34148                 case 3 :
34149                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34150                     break;
34151                 case 4 :
34152                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34153                     break;
34154                 default :
34155                     break;
34156             }
34157             
34158             Roo.each(box, function(b,kk){
34159                 
34160                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34161                 
34162                 var sz = b.el.getSize();
34163                 
34164                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34165                 
34166             }, this);
34167             
34168         }, this);
34169         
34170         var mY = 0;
34171         
34172         for (var i = 0; i < this.cols; i++){
34173             mY = Math.max(mY, maxY[i]);
34174         }
34175         
34176         this.el.setHeight(mY - pos.y);
34177         
34178     },
34179     
34180 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34181 //    {
34182 //        var pos = this.el.getBox(true);
34183 //        var x = pos.x;
34184 //        var y = pos.y;
34185 //        var maxX = pos.right;
34186 //        
34187 //        var maxHeight = 0;
34188 //        
34189 //        Roo.each(items, function(item, k){
34190 //            
34191 //            var c = k % 2;
34192 //            
34193 //            item.el.position('absolute');
34194 //                
34195 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34196 //
34197 //            item.el.setWidth(width);
34198 //
34199 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34200 //
34201 //            item.el.setHeight(height);
34202 //            
34203 //            if(c == 0){
34204 //                item.el.setXY([x, y], isInstant ? false : true);
34205 //            } else {
34206 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34207 //            }
34208 //            
34209 //            y = y + height + this.alternativePadWidth;
34210 //            
34211 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34212 //            
34213 //        }, this);
34214 //        
34215 //        this.el.setHeight(maxHeight);
34216 //        
34217 //    },
34218     
34219     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34220     {
34221         var pos = this.el.getBox(true);
34222         
34223         var minX = pos.x;
34224         var minY = pos.y;
34225         
34226         var maxX = pos.right;
34227         
34228         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34229         
34230         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34231         
34232         Roo.each(queue, function(box, k){
34233             
34234             Roo.each(box, function(b, kk){
34235                 
34236                 b.el.position('absolute');
34237                 
34238                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34239                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34240                 
34241                 if(b.size == 'md-left' || b.size == 'md-right'){
34242                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34243                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34244                 }
34245                 
34246                 b.el.setWidth(width);
34247                 b.el.setHeight(height);
34248                 
34249             }, this);
34250             
34251             if(!box.length){
34252                 return;
34253             }
34254             
34255             var positions = [];
34256             
34257             switch (box.length){
34258                 case 1 :
34259                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34260                     break;
34261                 case 2 :
34262                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34263                     break;
34264                 case 3 :
34265                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34266                     break;
34267                 case 4 :
34268                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34269                     break;
34270                 default :
34271                     break;
34272             }
34273             
34274             Roo.each(box, function(b,kk){
34275                 
34276                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34277                 
34278                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34279                 
34280             }, this);
34281             
34282         }, this);
34283         
34284     },
34285     
34286     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34287     {
34288         Roo.each(eItems, function(b,k){
34289             
34290             b.size = (k == 0) ? 'sm' : 'xs';
34291             b.x = (k == 0) ? 2 : 1;
34292             b.y = (k == 0) ? 2 : 1;
34293             
34294             b.el.position('absolute');
34295             
34296             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34297                 
34298             b.el.setWidth(width);
34299             
34300             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34301             
34302             b.el.setHeight(height);
34303             
34304         }, this);
34305
34306         var positions = [];
34307         
34308         positions.push({
34309             x : maxX - this.unitWidth * 2 - this.gutter,
34310             y : minY
34311         });
34312         
34313         positions.push({
34314             x : maxX - this.unitWidth,
34315             y : minY + (this.unitWidth + this.gutter) * 2
34316         });
34317         
34318         positions.push({
34319             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34320             y : minY
34321         });
34322         
34323         Roo.each(eItems, function(b,k){
34324             
34325             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34326
34327         }, this);
34328         
34329     },
34330     
34331     getVerticalOneBoxColPositions : function(x, y, box)
34332     {
34333         var pos = [];
34334         
34335         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34336         
34337         if(box[0].size == 'md-left'){
34338             rand = 0;
34339         }
34340         
34341         if(box[0].size == 'md-right'){
34342             rand = 1;
34343         }
34344         
34345         pos.push({
34346             x : x + (this.unitWidth + this.gutter) * rand,
34347             y : y
34348         });
34349         
34350         return pos;
34351     },
34352     
34353     getVerticalTwoBoxColPositions : function(x, y, box)
34354     {
34355         var pos = [];
34356         
34357         if(box[0].size == 'xs'){
34358             
34359             pos.push({
34360                 x : x,
34361                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34362             });
34363
34364             pos.push({
34365                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34366                 y : y
34367             });
34368             
34369             return pos;
34370             
34371         }
34372         
34373         pos.push({
34374             x : x,
34375             y : y
34376         });
34377
34378         pos.push({
34379             x : x + (this.unitWidth + this.gutter) * 2,
34380             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34381         });
34382         
34383         return pos;
34384         
34385     },
34386     
34387     getVerticalThreeBoxColPositions : function(x, y, box)
34388     {
34389         var pos = [];
34390         
34391         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34392             
34393             pos.push({
34394                 x : x,
34395                 y : y
34396             });
34397
34398             pos.push({
34399                 x : x + (this.unitWidth + this.gutter) * 1,
34400                 y : y
34401             });
34402             
34403             pos.push({
34404                 x : x + (this.unitWidth + this.gutter) * 2,
34405                 y : y
34406             });
34407             
34408             return pos;
34409             
34410         }
34411         
34412         if(box[0].size == 'xs' && box[1].size == 'xs'){
34413             
34414             pos.push({
34415                 x : x,
34416                 y : y
34417             });
34418
34419             pos.push({
34420                 x : x,
34421                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34422             });
34423             
34424             pos.push({
34425                 x : x + (this.unitWidth + this.gutter) * 1,
34426                 y : y
34427             });
34428             
34429             return pos;
34430             
34431         }
34432         
34433         pos.push({
34434             x : x,
34435             y : y
34436         });
34437
34438         pos.push({
34439             x : x + (this.unitWidth + this.gutter) * 2,
34440             y : y
34441         });
34442
34443         pos.push({
34444             x : x + (this.unitWidth + this.gutter) * 2,
34445             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34446         });
34447             
34448         return pos;
34449         
34450     },
34451     
34452     getVerticalFourBoxColPositions : function(x, y, box)
34453     {
34454         var pos = [];
34455         
34456         if(box[0].size == 'xs'){
34457             
34458             pos.push({
34459                 x : x,
34460                 y : y
34461             });
34462
34463             pos.push({
34464                 x : x,
34465                 y : y + (this.unitHeight + this.gutter) * 1
34466             });
34467             
34468             pos.push({
34469                 x : x,
34470                 y : y + (this.unitHeight + this.gutter) * 2
34471             });
34472             
34473             pos.push({
34474                 x : x + (this.unitWidth + this.gutter) * 1,
34475                 y : y
34476             });
34477             
34478             return pos;
34479             
34480         }
34481         
34482         pos.push({
34483             x : x,
34484             y : y
34485         });
34486
34487         pos.push({
34488             x : x + (this.unitWidth + this.gutter) * 2,
34489             y : y
34490         });
34491
34492         pos.push({
34493             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34494             y : y + (this.unitHeight + this.gutter) * 1
34495         });
34496
34497         pos.push({
34498             x : x + (this.unitWidth + this.gutter) * 2,
34499             y : y + (this.unitWidth + this.gutter) * 2
34500         });
34501
34502         return pos;
34503         
34504     },
34505     
34506     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34507     {
34508         var pos = [];
34509         
34510         if(box[0].size == 'md-left'){
34511             pos.push({
34512                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34513                 y : minY
34514             });
34515             
34516             return pos;
34517         }
34518         
34519         if(box[0].size == 'md-right'){
34520             pos.push({
34521                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34522                 y : minY + (this.unitWidth + this.gutter) * 1
34523             });
34524             
34525             return pos;
34526         }
34527         
34528         var rand = Math.floor(Math.random() * (4 - box[0].y));
34529         
34530         pos.push({
34531             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34532             y : minY + (this.unitWidth + this.gutter) * rand
34533         });
34534         
34535         return pos;
34536         
34537     },
34538     
34539     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34540     {
34541         var pos = [];
34542         
34543         if(box[0].size == 'xs'){
34544             
34545             pos.push({
34546                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34547                 y : minY
34548             });
34549
34550             pos.push({
34551                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34552                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34553             });
34554             
34555             return pos;
34556             
34557         }
34558         
34559         pos.push({
34560             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34561             y : minY
34562         });
34563
34564         pos.push({
34565             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34566             y : minY + (this.unitWidth + this.gutter) * 2
34567         });
34568         
34569         return pos;
34570         
34571     },
34572     
34573     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34574     {
34575         var pos = [];
34576         
34577         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34578             
34579             pos.push({
34580                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34581                 y : minY
34582             });
34583
34584             pos.push({
34585                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34586                 y : minY + (this.unitWidth + this.gutter) * 1
34587             });
34588             
34589             pos.push({
34590                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34591                 y : minY + (this.unitWidth + this.gutter) * 2
34592             });
34593             
34594             return pos;
34595             
34596         }
34597         
34598         if(box[0].size == 'xs' && box[1].size == 'xs'){
34599             
34600             pos.push({
34601                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34602                 y : minY
34603             });
34604
34605             pos.push({
34606                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34607                 y : minY
34608             });
34609             
34610             pos.push({
34611                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34612                 y : minY + (this.unitWidth + this.gutter) * 1
34613             });
34614             
34615             return pos;
34616             
34617         }
34618         
34619         pos.push({
34620             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34621             y : minY
34622         });
34623
34624         pos.push({
34625             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34626             y : minY + (this.unitWidth + this.gutter) * 2
34627         });
34628
34629         pos.push({
34630             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34631             y : minY + (this.unitWidth + this.gutter) * 2
34632         });
34633             
34634         return pos;
34635         
34636     },
34637     
34638     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34639     {
34640         var pos = [];
34641         
34642         if(box[0].size == 'xs'){
34643             
34644             pos.push({
34645                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34646                 y : minY
34647             });
34648
34649             pos.push({
34650                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34651                 y : minY
34652             });
34653             
34654             pos.push({
34655                 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),
34656                 y : minY
34657             });
34658             
34659             pos.push({
34660                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34661                 y : minY + (this.unitWidth + this.gutter) * 1
34662             });
34663             
34664             return pos;
34665             
34666         }
34667         
34668         pos.push({
34669             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34670             y : minY
34671         });
34672         
34673         pos.push({
34674             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34675             y : minY + (this.unitWidth + this.gutter) * 2
34676         });
34677         
34678         pos.push({
34679             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34680             y : minY + (this.unitWidth + this.gutter) * 2
34681         });
34682         
34683         pos.push({
34684             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),
34685             y : minY + (this.unitWidth + this.gutter) * 2
34686         });
34687
34688         return pos;
34689         
34690     },
34691     
34692     /**
34693     * remove a Masonry Brick
34694     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34695     */
34696     removeBrick : function(brick_id)
34697     {
34698         if (!brick_id) {
34699             return;
34700         }
34701         
34702         for (var i = 0; i<this.bricks.length; i++) {
34703             if (this.bricks[i].id == brick_id) {
34704                 this.bricks.splice(i,1);
34705                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34706                 this.initial();
34707             }
34708         }
34709     },
34710     
34711     /**
34712     * adds a Masonry Brick
34713     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34714     */
34715     addBrick : function(cfg)
34716     {
34717         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34718         //this.register(cn);
34719         cn.parentId = this.id;
34720         cn.render(this.el);
34721         return cn;
34722     },
34723     
34724     /**
34725     * register a Masonry Brick
34726     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34727     */
34728     
34729     register : function(brick)
34730     {
34731         this.bricks.push(brick);
34732         brick.masonryId = this.id;
34733     },
34734     
34735     /**
34736     * clear all the Masonry Brick
34737     */
34738     clearAll : function()
34739     {
34740         this.bricks = [];
34741         //this.getChildContainer().dom.innerHTML = "";
34742         this.el.dom.innerHTML = '';
34743     },
34744     
34745     getSelected : function()
34746     {
34747         if (!this.selectedBrick) {
34748             return false;
34749         }
34750         
34751         return this.selectedBrick;
34752     }
34753 });
34754
34755 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34756     
34757     groups: {},
34758      /**
34759     * register a Masonry Layout
34760     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34761     */
34762     
34763     register : function(layout)
34764     {
34765         this.groups[layout.id] = layout;
34766     },
34767     /**
34768     * fetch a  Masonry Layout based on the masonry layout ID
34769     * @param {string} the masonry layout to add
34770     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34771     */
34772     
34773     get: function(layout_id) {
34774         if (typeof(this.groups[layout_id]) == 'undefined') {
34775             return false;
34776         }
34777         return this.groups[layout_id] ;
34778     }
34779     
34780     
34781     
34782 });
34783
34784  
34785
34786  /**
34787  *
34788  * This is based on 
34789  * http://masonry.desandro.com
34790  *
34791  * The idea is to render all the bricks based on vertical width...
34792  *
34793  * The original code extends 'outlayer' - we might need to use that....
34794  * 
34795  */
34796
34797
34798 /**
34799  * @class Roo.bootstrap.LayoutMasonryAuto
34800  * @extends Roo.bootstrap.Component
34801  * Bootstrap Layout Masonry class
34802  * 
34803  * @constructor
34804  * Create a new Element
34805  * @param {Object} config The config object
34806  */
34807
34808 Roo.bootstrap.LayoutMasonryAuto = function(config){
34809     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34810 };
34811
34812 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34813     
34814       /**
34815      * @cfg {Boolean} isFitWidth  - resize the width..
34816      */   
34817     isFitWidth : false,  // options..
34818     /**
34819      * @cfg {Boolean} isOriginLeft = left align?
34820      */   
34821     isOriginLeft : true,
34822     /**
34823      * @cfg {Boolean} isOriginTop = top align?
34824      */   
34825     isOriginTop : false,
34826     /**
34827      * @cfg {Boolean} isLayoutInstant = no animation?
34828      */   
34829     isLayoutInstant : false, // needed?
34830     /**
34831      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34832      */   
34833     isResizingContainer : true,
34834     /**
34835      * @cfg {Number} columnWidth  width of the columns 
34836      */   
34837     
34838     columnWidth : 0,
34839     
34840     /**
34841      * @cfg {Number} maxCols maximum number of columns
34842      */   
34843     
34844     maxCols: 0,
34845     /**
34846      * @cfg {Number} padHeight padding below box..
34847      */   
34848     
34849     padHeight : 10, 
34850     
34851     /**
34852      * @cfg {Boolean} isAutoInitial defalut true
34853      */   
34854     
34855     isAutoInitial : true, 
34856     
34857     // private?
34858     gutter : 0,
34859     
34860     containerWidth: 0,
34861     initialColumnWidth : 0,
34862     currentSize : null,
34863     
34864     colYs : null, // array.
34865     maxY : 0,
34866     padWidth: 10,
34867     
34868     
34869     tag: 'div',
34870     cls: '',
34871     bricks: null, //CompositeElement
34872     cols : 0, // array?
34873     // element : null, // wrapped now this.el
34874     _isLayoutInited : null, 
34875     
34876     
34877     getAutoCreate : function(){
34878         
34879         var cfg = {
34880             tag: this.tag,
34881             cls: 'blog-masonary-wrapper ' + this.cls,
34882             cn : {
34883                 cls : 'mas-boxes masonary'
34884             }
34885         };
34886         
34887         return cfg;
34888     },
34889     
34890     getChildContainer: function( )
34891     {
34892         if (this.boxesEl) {
34893             return this.boxesEl;
34894         }
34895         
34896         this.boxesEl = this.el.select('.mas-boxes').first();
34897         
34898         return this.boxesEl;
34899     },
34900     
34901     
34902     initEvents : function()
34903     {
34904         var _this = this;
34905         
34906         if(this.isAutoInitial){
34907             Roo.log('hook children rendered');
34908             this.on('childrenrendered', function() {
34909                 Roo.log('children rendered');
34910                 _this.initial();
34911             } ,this);
34912         }
34913         
34914     },
34915     
34916     initial : function()
34917     {
34918         this.reloadItems();
34919
34920         this.currentSize = this.el.getBox(true);
34921
34922         /// was window resize... - let's see if this works..
34923         Roo.EventManager.onWindowResize(this.resize, this); 
34924
34925         if(!this.isAutoInitial){
34926             this.layout();
34927             return;
34928         }
34929         
34930         this.layout.defer(500,this);
34931     },
34932     
34933     reloadItems: function()
34934     {
34935         this.bricks = this.el.select('.masonry-brick', true);
34936         
34937         this.bricks.each(function(b) {
34938             //Roo.log(b.getSize());
34939             if (!b.attr('originalwidth')) {
34940                 b.attr('originalwidth',  b.getSize().width);
34941             }
34942             
34943         });
34944         
34945         Roo.log(this.bricks.elements.length);
34946     },
34947     
34948     resize : function()
34949     {
34950         Roo.log('resize');
34951         var cs = this.el.getBox(true);
34952         
34953         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34954             Roo.log("no change in with or X");
34955             return;
34956         }
34957         this.currentSize = cs;
34958         this.layout();
34959     },
34960     
34961     layout : function()
34962     {
34963          Roo.log('layout');
34964         this._resetLayout();
34965         //this._manageStamps();
34966       
34967         // don't animate first layout
34968         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34969         this.layoutItems( isInstant );
34970       
34971         // flag for initalized
34972         this._isLayoutInited = true;
34973     },
34974     
34975     layoutItems : function( isInstant )
34976     {
34977         //var items = this._getItemsForLayout( this.items );
34978         // original code supports filtering layout items.. we just ignore it..
34979         
34980         this._layoutItems( this.bricks , isInstant );
34981       
34982         this._postLayout();
34983     },
34984     _layoutItems : function ( items , isInstant)
34985     {
34986        //this.fireEvent( 'layout', this, items );
34987     
34988
34989         if ( !items || !items.elements.length ) {
34990           // no items, emit event with empty array
34991             return;
34992         }
34993
34994         var queue = [];
34995         items.each(function(item) {
34996             Roo.log("layout item");
34997             Roo.log(item);
34998             // get x/y object from method
34999             var position = this._getItemLayoutPosition( item );
35000             // enqueue
35001             position.item = item;
35002             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35003             queue.push( position );
35004         }, this);
35005       
35006         this._processLayoutQueue( queue );
35007     },
35008     /** Sets position of item in DOM
35009     * @param {Element} item
35010     * @param {Number} x - horizontal position
35011     * @param {Number} y - vertical position
35012     * @param {Boolean} isInstant - disables transitions
35013     */
35014     _processLayoutQueue : function( queue )
35015     {
35016         for ( var i=0, len = queue.length; i < len; i++ ) {
35017             var obj = queue[i];
35018             obj.item.position('absolute');
35019             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35020         }
35021     },
35022       
35023     
35024     /**
35025     * Any logic you want to do after each layout,
35026     * i.e. size the container
35027     */
35028     _postLayout : function()
35029     {
35030         this.resizeContainer();
35031     },
35032     
35033     resizeContainer : function()
35034     {
35035         if ( !this.isResizingContainer ) {
35036             return;
35037         }
35038         var size = this._getContainerSize();
35039         if ( size ) {
35040             this.el.setSize(size.width,size.height);
35041             this.boxesEl.setSize(size.width,size.height);
35042         }
35043     },
35044     
35045     
35046     
35047     _resetLayout : function()
35048     {
35049         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35050         this.colWidth = this.el.getWidth();
35051         //this.gutter = this.el.getWidth(); 
35052         
35053         this.measureColumns();
35054
35055         // reset column Y
35056         var i = this.cols;
35057         this.colYs = [];
35058         while (i--) {
35059             this.colYs.push( 0 );
35060         }
35061     
35062         this.maxY = 0;
35063     },
35064
35065     measureColumns : function()
35066     {
35067         this.getContainerWidth();
35068       // if columnWidth is 0, default to outerWidth of first item
35069         if ( !this.columnWidth ) {
35070             var firstItem = this.bricks.first();
35071             Roo.log(firstItem);
35072             this.columnWidth  = this.containerWidth;
35073             if (firstItem && firstItem.attr('originalwidth') ) {
35074                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35075             }
35076             // columnWidth fall back to item of first element
35077             Roo.log("set column width?");
35078                         this.initialColumnWidth = this.columnWidth  ;
35079
35080             // if first elem has no width, default to size of container
35081             
35082         }
35083         
35084         
35085         if (this.initialColumnWidth) {
35086             this.columnWidth = this.initialColumnWidth;
35087         }
35088         
35089         
35090             
35091         // column width is fixed at the top - however if container width get's smaller we should
35092         // reduce it...
35093         
35094         // this bit calcs how man columns..
35095             
35096         var columnWidth = this.columnWidth += this.gutter;
35097       
35098         // calculate columns
35099         var containerWidth = this.containerWidth + this.gutter;
35100         
35101         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35102         // fix rounding errors, typically with gutters
35103         var excess = columnWidth - containerWidth % columnWidth;
35104         
35105         
35106         // if overshoot is less than a pixel, round up, otherwise floor it
35107         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35108         cols = Math[ mathMethod ]( cols );
35109         this.cols = Math.max( cols, 1 );
35110         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35111         
35112          // padding positioning..
35113         var totalColWidth = this.cols * this.columnWidth;
35114         var padavail = this.containerWidth - totalColWidth;
35115         // so for 2 columns - we need 3 'pads'
35116         
35117         var padNeeded = (1+this.cols) * this.padWidth;
35118         
35119         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35120         
35121         this.columnWidth += padExtra
35122         //this.padWidth = Math.floor(padavail /  ( this.cols));
35123         
35124         // adjust colum width so that padding is fixed??
35125         
35126         // we have 3 columns ... total = width * 3
35127         // we have X left over... that should be used by 
35128         
35129         //if (this.expandC) {
35130             
35131         //}
35132         
35133         
35134         
35135     },
35136     
35137     getContainerWidth : function()
35138     {
35139        /* // container is parent if fit width
35140         var container = this.isFitWidth ? this.element.parentNode : this.element;
35141         // check that this.size and size are there
35142         // IE8 triggers resize on body size change, so they might not be
35143         
35144         var size = getSize( container );  //FIXME
35145         this.containerWidth = size && size.innerWidth; //FIXME
35146         */
35147          
35148         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35149         
35150     },
35151     
35152     _getItemLayoutPosition : function( item )  // what is item?
35153     {
35154         // we resize the item to our columnWidth..
35155       
35156         item.setWidth(this.columnWidth);
35157         item.autoBoxAdjust  = false;
35158         
35159         var sz = item.getSize();
35160  
35161         // how many columns does this brick span
35162         var remainder = this.containerWidth % this.columnWidth;
35163         
35164         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35165         // round if off by 1 pixel, otherwise use ceil
35166         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35167         colSpan = Math.min( colSpan, this.cols );
35168         
35169         // normally this should be '1' as we dont' currently allow multi width columns..
35170         
35171         var colGroup = this._getColGroup( colSpan );
35172         // get the minimum Y value from the columns
35173         var minimumY = Math.min.apply( Math, colGroup );
35174         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35175         
35176         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35177          
35178         // position the brick
35179         var position = {
35180             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35181             y: this.currentSize.y + minimumY + this.padHeight
35182         };
35183         
35184         Roo.log(position);
35185         // apply setHeight to necessary columns
35186         var setHeight = minimumY + sz.height + this.padHeight;
35187         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35188         
35189         var setSpan = this.cols + 1 - colGroup.length;
35190         for ( var i = 0; i < setSpan; i++ ) {
35191           this.colYs[ shortColIndex + i ] = setHeight ;
35192         }
35193       
35194         return position;
35195     },
35196     
35197     /**
35198      * @param {Number} colSpan - number of columns the element spans
35199      * @returns {Array} colGroup
35200      */
35201     _getColGroup : function( colSpan )
35202     {
35203         if ( colSpan < 2 ) {
35204           // if brick spans only one column, use all the column Ys
35205           return this.colYs;
35206         }
35207       
35208         var colGroup = [];
35209         // how many different places could this brick fit horizontally
35210         var groupCount = this.cols + 1 - colSpan;
35211         // for each group potential horizontal position
35212         for ( var i = 0; i < groupCount; i++ ) {
35213           // make an array of colY values for that one group
35214           var groupColYs = this.colYs.slice( i, i + colSpan );
35215           // and get the max value of the array
35216           colGroup[i] = Math.max.apply( Math, groupColYs );
35217         }
35218         return colGroup;
35219     },
35220     /*
35221     _manageStamp : function( stamp )
35222     {
35223         var stampSize =  stamp.getSize();
35224         var offset = stamp.getBox();
35225         // get the columns that this stamp affects
35226         var firstX = this.isOriginLeft ? offset.x : offset.right;
35227         var lastX = firstX + stampSize.width;
35228         var firstCol = Math.floor( firstX / this.columnWidth );
35229         firstCol = Math.max( 0, firstCol );
35230         
35231         var lastCol = Math.floor( lastX / this.columnWidth );
35232         // lastCol should not go over if multiple of columnWidth #425
35233         lastCol -= lastX % this.columnWidth ? 0 : 1;
35234         lastCol = Math.min( this.cols - 1, lastCol );
35235         
35236         // set colYs to bottom of the stamp
35237         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35238             stampSize.height;
35239             
35240         for ( var i = firstCol; i <= lastCol; i++ ) {
35241           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35242         }
35243     },
35244     */
35245     
35246     _getContainerSize : function()
35247     {
35248         this.maxY = Math.max.apply( Math, this.colYs );
35249         var size = {
35250             height: this.maxY
35251         };
35252       
35253         if ( this.isFitWidth ) {
35254             size.width = this._getContainerFitWidth();
35255         }
35256       
35257         return size;
35258     },
35259     
35260     _getContainerFitWidth : function()
35261     {
35262         var unusedCols = 0;
35263         // count unused columns
35264         var i = this.cols;
35265         while ( --i ) {
35266           if ( this.colYs[i] !== 0 ) {
35267             break;
35268           }
35269           unusedCols++;
35270         }
35271         // fit container to columns that have been used
35272         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35273     },
35274     
35275     needsResizeLayout : function()
35276     {
35277         var previousWidth = this.containerWidth;
35278         this.getContainerWidth();
35279         return previousWidth !== this.containerWidth;
35280     }
35281  
35282 });
35283
35284  
35285
35286  /*
35287  * - LGPL
35288  *
35289  * element
35290  * 
35291  */
35292
35293 /**
35294  * @class Roo.bootstrap.MasonryBrick
35295  * @extends Roo.bootstrap.Component
35296  * Bootstrap MasonryBrick class
35297  * 
35298  * @constructor
35299  * Create a new MasonryBrick
35300  * @param {Object} config The config object
35301  */
35302
35303 Roo.bootstrap.MasonryBrick = function(config){
35304     
35305     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35306     
35307     Roo.bootstrap.MasonryBrick.register(this);
35308     
35309     this.addEvents({
35310         // raw events
35311         /**
35312          * @event click
35313          * When a MasonryBrick is clcik
35314          * @param {Roo.bootstrap.MasonryBrick} this
35315          * @param {Roo.EventObject} e
35316          */
35317         "click" : true
35318     });
35319 };
35320
35321 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35322     
35323     /**
35324      * @cfg {String} title
35325      */   
35326     title : '',
35327     /**
35328      * @cfg {String} html
35329      */   
35330     html : '',
35331     /**
35332      * @cfg {String} bgimage
35333      */   
35334     bgimage : '',
35335     /**
35336      * @cfg {String} videourl
35337      */   
35338     videourl : '',
35339     /**
35340      * @cfg {String} cls
35341      */   
35342     cls : '',
35343     /**
35344      * @cfg {String} href
35345      */   
35346     href : '',
35347     /**
35348      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35349      */   
35350     size : 'xs',
35351     
35352     /**
35353      * @cfg {String} placetitle (center|bottom)
35354      */   
35355     placetitle : '',
35356     
35357     /**
35358      * @cfg {Boolean} isFitContainer defalut true
35359      */   
35360     isFitContainer : true, 
35361     
35362     /**
35363      * @cfg {Boolean} preventDefault defalut false
35364      */   
35365     preventDefault : false, 
35366     
35367     /**
35368      * @cfg {Boolean} inverse defalut false
35369      */   
35370     maskInverse : false, 
35371     
35372     getAutoCreate : function()
35373     {
35374         if(!this.isFitContainer){
35375             return this.getSplitAutoCreate();
35376         }
35377         
35378         var cls = 'masonry-brick masonry-brick-full';
35379         
35380         if(this.href.length){
35381             cls += ' masonry-brick-link';
35382         }
35383         
35384         if(this.bgimage.length){
35385             cls += ' masonry-brick-image';
35386         }
35387         
35388         if(this.maskInverse){
35389             cls += ' mask-inverse';
35390         }
35391         
35392         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35393             cls += ' enable-mask';
35394         }
35395         
35396         if(this.size){
35397             cls += ' masonry-' + this.size + '-brick';
35398         }
35399         
35400         if(this.placetitle.length){
35401             
35402             switch (this.placetitle) {
35403                 case 'center' :
35404                     cls += ' masonry-center-title';
35405                     break;
35406                 case 'bottom' :
35407                     cls += ' masonry-bottom-title';
35408                     break;
35409                 default:
35410                     break;
35411             }
35412             
35413         } else {
35414             if(!this.html.length && !this.bgimage.length){
35415                 cls += ' masonry-center-title';
35416             }
35417
35418             if(!this.html.length && this.bgimage.length){
35419                 cls += ' masonry-bottom-title';
35420             }
35421         }
35422         
35423         if(this.cls){
35424             cls += ' ' + this.cls;
35425         }
35426         
35427         var cfg = {
35428             tag: (this.href.length) ? 'a' : 'div',
35429             cls: cls,
35430             cn: [
35431                 {
35432                     tag: 'div',
35433                     cls: 'masonry-brick-mask'
35434                 },
35435                 {
35436                     tag: 'div',
35437                     cls: 'masonry-brick-paragraph',
35438                     cn: []
35439                 }
35440             ]
35441         };
35442         
35443         if(this.href.length){
35444             cfg.href = this.href;
35445         }
35446         
35447         var cn = cfg.cn[1].cn;
35448         
35449         if(this.title.length){
35450             cn.push({
35451                 tag: 'h4',
35452                 cls: 'masonry-brick-title',
35453                 html: this.title
35454             });
35455         }
35456         
35457         if(this.html.length){
35458             cn.push({
35459                 tag: 'p',
35460                 cls: 'masonry-brick-text',
35461                 html: this.html
35462             });
35463         }
35464         
35465         if (!this.title.length && !this.html.length) {
35466             cfg.cn[1].cls += ' hide';
35467         }
35468         
35469         if(this.bgimage.length){
35470             cfg.cn.push({
35471                 tag: 'img',
35472                 cls: 'masonry-brick-image-view',
35473                 src: this.bgimage
35474             });
35475         }
35476         
35477         if(this.videourl.length){
35478             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35479             // youtube support only?
35480             cfg.cn.push({
35481                 tag: 'iframe',
35482                 cls: 'masonry-brick-image-view',
35483                 src: vurl,
35484                 frameborder : 0,
35485                 allowfullscreen : true
35486             });
35487         }
35488         
35489         return cfg;
35490         
35491     },
35492     
35493     getSplitAutoCreate : function()
35494     {
35495         var cls = 'masonry-brick masonry-brick-split';
35496         
35497         if(this.href.length){
35498             cls += ' masonry-brick-link';
35499         }
35500         
35501         if(this.bgimage.length){
35502             cls += ' masonry-brick-image';
35503         }
35504         
35505         if(this.size){
35506             cls += ' masonry-' + this.size + '-brick';
35507         }
35508         
35509         switch (this.placetitle) {
35510             case 'center' :
35511                 cls += ' masonry-center-title';
35512                 break;
35513             case 'bottom' :
35514                 cls += ' masonry-bottom-title';
35515                 break;
35516             default:
35517                 if(!this.bgimage.length){
35518                     cls += ' masonry-center-title';
35519                 }
35520
35521                 if(this.bgimage.length){
35522                     cls += ' masonry-bottom-title';
35523                 }
35524                 break;
35525         }
35526         
35527         if(this.cls){
35528             cls += ' ' + this.cls;
35529         }
35530         
35531         var cfg = {
35532             tag: (this.href.length) ? 'a' : 'div',
35533             cls: cls,
35534             cn: [
35535                 {
35536                     tag: 'div',
35537                     cls: 'masonry-brick-split-head',
35538                     cn: [
35539                         {
35540                             tag: 'div',
35541                             cls: 'masonry-brick-paragraph',
35542                             cn: []
35543                         }
35544                     ]
35545                 },
35546                 {
35547                     tag: 'div',
35548                     cls: 'masonry-brick-split-body',
35549                     cn: []
35550                 }
35551             ]
35552         };
35553         
35554         if(this.href.length){
35555             cfg.href = this.href;
35556         }
35557         
35558         if(this.title.length){
35559             cfg.cn[0].cn[0].cn.push({
35560                 tag: 'h4',
35561                 cls: 'masonry-brick-title',
35562                 html: this.title
35563             });
35564         }
35565         
35566         if(this.html.length){
35567             cfg.cn[1].cn.push({
35568                 tag: 'p',
35569                 cls: 'masonry-brick-text',
35570                 html: this.html
35571             });
35572         }
35573
35574         if(this.bgimage.length){
35575             cfg.cn[0].cn.push({
35576                 tag: 'img',
35577                 cls: 'masonry-brick-image-view',
35578                 src: this.bgimage
35579             });
35580         }
35581         
35582         if(this.videourl.length){
35583             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35584             // youtube support only?
35585             cfg.cn[0].cn.cn.push({
35586                 tag: 'iframe',
35587                 cls: 'masonry-brick-image-view',
35588                 src: vurl,
35589                 frameborder : 0,
35590                 allowfullscreen : true
35591             });
35592         }
35593         
35594         return cfg;
35595     },
35596     
35597     initEvents: function() 
35598     {
35599         switch (this.size) {
35600             case 'xs' :
35601                 this.x = 1;
35602                 this.y = 1;
35603                 break;
35604             case 'sm' :
35605                 this.x = 2;
35606                 this.y = 2;
35607                 break;
35608             case 'md' :
35609             case 'md-left' :
35610             case 'md-right' :
35611                 this.x = 3;
35612                 this.y = 3;
35613                 break;
35614             case 'tall' :
35615                 this.x = 2;
35616                 this.y = 3;
35617                 break;
35618             case 'wide' :
35619                 this.x = 3;
35620                 this.y = 2;
35621                 break;
35622             case 'wide-thin' :
35623                 this.x = 3;
35624                 this.y = 1;
35625                 break;
35626                         
35627             default :
35628                 break;
35629         }
35630         
35631         if(Roo.isTouch){
35632             this.el.on('touchstart', this.onTouchStart, this);
35633             this.el.on('touchmove', this.onTouchMove, this);
35634             this.el.on('touchend', this.onTouchEnd, this);
35635             this.el.on('contextmenu', this.onContextMenu, this);
35636         } else {
35637             this.el.on('mouseenter'  ,this.enter, this);
35638             this.el.on('mouseleave', this.leave, this);
35639             this.el.on('click', this.onClick, this);
35640         }
35641         
35642         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35643             this.parent().bricks.push(this);   
35644         }
35645         
35646     },
35647     
35648     onClick: function(e, el)
35649     {
35650         var time = this.endTimer - this.startTimer;
35651         // Roo.log(e.preventDefault());
35652         if(Roo.isTouch){
35653             if(time > 1000){
35654                 e.preventDefault();
35655                 return;
35656             }
35657         }
35658         
35659         if(!this.preventDefault){
35660             return;
35661         }
35662         
35663         e.preventDefault();
35664         
35665         if (this.activeClass != '') {
35666             this.selectBrick();
35667         }
35668         
35669         this.fireEvent('click', this, e);
35670     },
35671     
35672     enter: function(e, el)
35673     {
35674         e.preventDefault();
35675         
35676         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35677             return;
35678         }
35679         
35680         if(this.bgimage.length && this.html.length){
35681             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35682         }
35683     },
35684     
35685     leave: function(e, el)
35686     {
35687         e.preventDefault();
35688         
35689         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35690             return;
35691         }
35692         
35693         if(this.bgimage.length && this.html.length){
35694             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35695         }
35696     },
35697     
35698     onTouchStart: function(e, el)
35699     {
35700 //        e.preventDefault();
35701         
35702         this.touchmoved = false;
35703         
35704         if(!this.isFitContainer){
35705             return;
35706         }
35707         
35708         if(!this.bgimage.length || !this.html.length){
35709             return;
35710         }
35711         
35712         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35713         
35714         this.timer = new Date().getTime();
35715         
35716     },
35717     
35718     onTouchMove: function(e, el)
35719     {
35720         this.touchmoved = true;
35721     },
35722     
35723     onContextMenu : function(e,el)
35724     {
35725         e.preventDefault();
35726         e.stopPropagation();
35727         return false;
35728     },
35729     
35730     onTouchEnd: function(e, el)
35731     {
35732 //        e.preventDefault();
35733         
35734         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35735         
35736             this.leave(e,el);
35737             
35738             return;
35739         }
35740         
35741         if(!this.bgimage.length || !this.html.length){
35742             
35743             if(this.href.length){
35744                 window.location.href = this.href;
35745             }
35746             
35747             return;
35748         }
35749         
35750         if(!this.isFitContainer){
35751             return;
35752         }
35753         
35754         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35755         
35756         window.location.href = this.href;
35757     },
35758     
35759     //selection on single brick only
35760     selectBrick : function() {
35761         
35762         if (!this.parentId) {
35763             return;
35764         }
35765         
35766         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35767         var index = m.selectedBrick.indexOf(this.id);
35768         
35769         if ( index > -1) {
35770             m.selectedBrick.splice(index,1);
35771             this.el.removeClass(this.activeClass);
35772             return;
35773         }
35774         
35775         for(var i = 0; i < m.selectedBrick.length; i++) {
35776             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35777             b.el.removeClass(b.activeClass);
35778         }
35779         
35780         m.selectedBrick = [];
35781         
35782         m.selectedBrick.push(this.id);
35783         this.el.addClass(this.activeClass);
35784         return;
35785     },
35786     
35787     isSelected : function(){
35788         return this.el.hasClass(this.activeClass);
35789         
35790     }
35791 });
35792
35793 Roo.apply(Roo.bootstrap.MasonryBrick, {
35794     
35795     //groups: {},
35796     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35797      /**
35798     * register a Masonry Brick
35799     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35800     */
35801     
35802     register : function(brick)
35803     {
35804         //this.groups[brick.id] = brick;
35805         this.groups.add(brick.id, brick);
35806     },
35807     /**
35808     * fetch a  masonry brick based on the masonry brick ID
35809     * @param {string} the masonry brick to add
35810     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35811     */
35812     
35813     get: function(brick_id) 
35814     {
35815         // if (typeof(this.groups[brick_id]) == 'undefined') {
35816         //     return false;
35817         // }
35818         // return this.groups[brick_id] ;
35819         
35820         if(this.groups.key(brick_id)) {
35821             return this.groups.key(brick_id);
35822         }
35823         
35824         return false;
35825     }
35826     
35827     
35828     
35829 });
35830
35831  /*
35832  * - LGPL
35833  *
35834  * element
35835  * 
35836  */
35837
35838 /**
35839  * @class Roo.bootstrap.Brick
35840  * @extends Roo.bootstrap.Component
35841  * Bootstrap Brick class
35842  * 
35843  * @constructor
35844  * Create a new Brick
35845  * @param {Object} config The config object
35846  */
35847
35848 Roo.bootstrap.Brick = function(config){
35849     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35850     
35851     this.addEvents({
35852         // raw events
35853         /**
35854          * @event click
35855          * When a Brick is click
35856          * @param {Roo.bootstrap.Brick} this
35857          * @param {Roo.EventObject} e
35858          */
35859         "click" : true
35860     });
35861 };
35862
35863 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35864     
35865     /**
35866      * @cfg {String} title
35867      */   
35868     title : '',
35869     /**
35870      * @cfg {String} html
35871      */   
35872     html : '',
35873     /**
35874      * @cfg {String} bgimage
35875      */   
35876     bgimage : '',
35877     /**
35878      * @cfg {String} cls
35879      */   
35880     cls : '',
35881     /**
35882      * @cfg {String} href
35883      */   
35884     href : '',
35885     /**
35886      * @cfg {String} video
35887      */   
35888     video : '',
35889     /**
35890      * @cfg {Boolean} square
35891      */   
35892     square : true,
35893     
35894     getAutoCreate : function()
35895     {
35896         var cls = 'roo-brick';
35897         
35898         if(this.href.length){
35899             cls += ' roo-brick-link';
35900         }
35901         
35902         if(this.bgimage.length){
35903             cls += ' roo-brick-image';
35904         }
35905         
35906         if(!this.html.length && !this.bgimage.length){
35907             cls += ' roo-brick-center-title';
35908         }
35909         
35910         if(!this.html.length && this.bgimage.length){
35911             cls += ' roo-brick-bottom-title';
35912         }
35913         
35914         if(this.cls){
35915             cls += ' ' + this.cls;
35916         }
35917         
35918         var cfg = {
35919             tag: (this.href.length) ? 'a' : 'div',
35920             cls: cls,
35921             cn: [
35922                 {
35923                     tag: 'div',
35924                     cls: 'roo-brick-paragraph',
35925                     cn: []
35926                 }
35927             ]
35928         };
35929         
35930         if(this.href.length){
35931             cfg.href = this.href;
35932         }
35933         
35934         var cn = cfg.cn[0].cn;
35935         
35936         if(this.title.length){
35937             cn.push({
35938                 tag: 'h4',
35939                 cls: 'roo-brick-title',
35940                 html: this.title
35941             });
35942         }
35943         
35944         if(this.html.length){
35945             cn.push({
35946                 tag: 'p',
35947                 cls: 'roo-brick-text',
35948                 html: this.html
35949             });
35950         } else {
35951             cn.cls += ' hide';
35952         }
35953         
35954         if(this.bgimage.length){
35955             cfg.cn.push({
35956                 tag: 'img',
35957                 cls: 'roo-brick-image-view',
35958                 src: this.bgimage
35959             });
35960         }
35961         
35962         return cfg;
35963     },
35964     
35965     initEvents: function() 
35966     {
35967         if(this.title.length || this.html.length){
35968             this.el.on('mouseenter'  ,this.enter, this);
35969             this.el.on('mouseleave', this.leave, this);
35970         }
35971         
35972         Roo.EventManager.onWindowResize(this.resize, this); 
35973         
35974         if(this.bgimage.length){
35975             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35976             this.imageEl.on('load', this.onImageLoad, this);
35977             return;
35978         }
35979         
35980         this.resize();
35981     },
35982     
35983     onImageLoad : function()
35984     {
35985         this.resize();
35986     },
35987     
35988     resize : function()
35989     {
35990         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35991         
35992         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35993         
35994         if(this.bgimage.length){
35995             var image = this.el.select('.roo-brick-image-view', true).first();
35996             
35997             image.setWidth(paragraph.getWidth());
35998             
35999             if(this.square){
36000                 image.setHeight(paragraph.getWidth());
36001             }
36002             
36003             this.el.setHeight(image.getHeight());
36004             paragraph.setHeight(image.getHeight());
36005             
36006         }
36007         
36008     },
36009     
36010     enter: function(e, el)
36011     {
36012         e.preventDefault();
36013         
36014         if(this.bgimage.length){
36015             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36016             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36017         }
36018     },
36019     
36020     leave: function(e, el)
36021     {
36022         e.preventDefault();
36023         
36024         if(this.bgimage.length){
36025             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36026             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36027         }
36028     }
36029     
36030 });
36031
36032  
36033
36034  /*
36035  * - LGPL
36036  *
36037  * Number field 
36038  */
36039
36040 /**
36041  * @class Roo.bootstrap.NumberField
36042  * @extends Roo.bootstrap.Input
36043  * Bootstrap NumberField class
36044  * 
36045  * 
36046  * 
36047  * 
36048  * @constructor
36049  * Create a new NumberField
36050  * @param {Object} config The config object
36051  */
36052
36053 Roo.bootstrap.NumberField = function(config){
36054     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36055 };
36056
36057 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36058     
36059     /**
36060      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36061      */
36062     allowDecimals : true,
36063     /**
36064      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36065      */
36066     decimalSeparator : ".",
36067     /**
36068      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36069      */
36070     decimalPrecision : 2,
36071     /**
36072      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36073      */
36074     allowNegative : true,
36075     
36076     /**
36077      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36078      */
36079     allowZero: true,
36080     /**
36081      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36082      */
36083     minValue : Number.NEGATIVE_INFINITY,
36084     /**
36085      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36086      */
36087     maxValue : Number.MAX_VALUE,
36088     /**
36089      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36090      */
36091     minText : "The minimum value for this field is {0}",
36092     /**
36093      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36094      */
36095     maxText : "The maximum value for this field is {0}",
36096     /**
36097      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36098      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36099      */
36100     nanText : "{0} is not a valid number",
36101     /**
36102      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36103      */
36104     thousandsDelimiter : false,
36105     /**
36106      * @cfg {String} valueAlign alignment of value
36107      */
36108     valueAlign : "left",
36109
36110     getAutoCreate : function()
36111     {
36112         var hiddenInput = {
36113             tag: 'input',
36114             type: 'hidden',
36115             id: Roo.id(),
36116             cls: 'hidden-number-input'
36117         };
36118         
36119         if (this.name) {
36120             hiddenInput.name = this.name;
36121         }
36122         
36123         this.name = '';
36124         
36125         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36126         
36127         this.name = hiddenInput.name;
36128         
36129         if(cfg.cn.length > 0) {
36130             cfg.cn.push(hiddenInput);
36131         }
36132         
36133         return cfg;
36134     },
36135
36136     // private
36137     initEvents : function()
36138     {   
36139         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36140         
36141         var allowed = "0123456789";
36142         
36143         if(this.allowDecimals){
36144             allowed += this.decimalSeparator;
36145         }
36146         
36147         if(this.allowNegative){
36148             allowed += "-";
36149         }
36150         
36151         if(this.thousandsDelimiter) {
36152             allowed += ",";
36153         }
36154         
36155         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36156         
36157         var keyPress = function(e){
36158             
36159             var k = e.getKey();
36160             
36161             var c = e.getCharCode();
36162             
36163             if(
36164                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36165                     allowed.indexOf(String.fromCharCode(c)) === -1
36166             ){
36167                 e.stopEvent();
36168                 return;
36169             }
36170             
36171             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36172                 return;
36173             }
36174             
36175             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36176                 e.stopEvent();
36177             }
36178         };
36179         
36180         this.el.on("keypress", keyPress, this);
36181     },
36182     
36183     validateValue : function(value)
36184     {
36185         
36186         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36187             return false;
36188         }
36189         
36190         var num = this.parseValue(value);
36191         
36192         if(isNaN(num)){
36193             this.markInvalid(String.format(this.nanText, value));
36194             return false;
36195         }
36196         
36197         if(num < this.minValue){
36198             this.markInvalid(String.format(this.minText, this.minValue));
36199             return false;
36200         }
36201         
36202         if(num > this.maxValue){
36203             this.markInvalid(String.format(this.maxText, this.maxValue));
36204             return false;
36205         }
36206         
36207         return true;
36208     },
36209
36210     getValue : function()
36211     {
36212         var v = this.hiddenEl().getValue();
36213         
36214         return this.fixPrecision(this.parseValue(v));
36215     },
36216
36217     parseValue : function(value)
36218     {
36219         if(this.thousandsDelimiter) {
36220             value += "";
36221             r = new RegExp(",", "g");
36222             value = value.replace(r, "");
36223         }
36224         
36225         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36226         return isNaN(value) ? '' : value;
36227     },
36228
36229     fixPrecision : function(value)
36230     {
36231         if(this.thousandsDelimiter) {
36232             value += "";
36233             r = new RegExp(",", "g");
36234             value = value.replace(r, "");
36235         }
36236         
36237         var nan = isNaN(value);
36238         
36239         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36240             return nan ? '' : value;
36241         }
36242         return parseFloat(value).toFixed(this.decimalPrecision);
36243     },
36244
36245     setValue : function(v)
36246     {
36247         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36248         
36249         this.value = v;
36250         
36251         if(this.rendered){
36252             
36253             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36254             
36255             this.inputEl().dom.value = (v == '') ? '' :
36256                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36257             
36258             if(!this.allowZero && v === '0') {
36259                 this.hiddenEl().dom.value = '';
36260                 this.inputEl().dom.value = '';
36261             }
36262             
36263             this.validate();
36264         }
36265     },
36266
36267     decimalPrecisionFcn : function(v)
36268     {
36269         return Math.floor(v);
36270     },
36271
36272     beforeBlur : function()
36273     {
36274         var v = this.parseValue(this.getRawValue());
36275         
36276         if(v || v === 0 || v === ''){
36277             this.setValue(v);
36278         }
36279     },
36280     
36281     hiddenEl : function()
36282     {
36283         return this.el.select('input.hidden-number-input',true).first();
36284     }
36285     
36286 });
36287
36288  
36289
36290 /*
36291 * Licence: LGPL
36292 */
36293
36294 /**
36295  * @class Roo.bootstrap.DocumentSlider
36296  * @extends Roo.bootstrap.Component
36297  * Bootstrap DocumentSlider class
36298  * 
36299  * @constructor
36300  * Create a new DocumentViewer
36301  * @param {Object} config The config object
36302  */
36303
36304 Roo.bootstrap.DocumentSlider = function(config){
36305     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36306     
36307     this.files = [];
36308     
36309     this.addEvents({
36310         /**
36311          * @event initial
36312          * Fire after initEvent
36313          * @param {Roo.bootstrap.DocumentSlider} this
36314          */
36315         "initial" : true,
36316         /**
36317          * @event update
36318          * Fire after update
36319          * @param {Roo.bootstrap.DocumentSlider} this
36320          */
36321         "update" : true,
36322         /**
36323          * @event click
36324          * Fire after click
36325          * @param {Roo.bootstrap.DocumentSlider} this
36326          */
36327         "click" : true
36328     });
36329 };
36330
36331 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36332     
36333     files : false,
36334     
36335     indicator : 0,
36336     
36337     getAutoCreate : function()
36338     {
36339         var cfg = {
36340             tag : 'div',
36341             cls : 'roo-document-slider',
36342             cn : [
36343                 {
36344                     tag : 'div',
36345                     cls : 'roo-document-slider-header',
36346                     cn : [
36347                         {
36348                             tag : 'div',
36349                             cls : 'roo-document-slider-header-title'
36350                         }
36351                     ]
36352                 },
36353                 {
36354                     tag : 'div',
36355                     cls : 'roo-document-slider-body',
36356                     cn : [
36357                         {
36358                             tag : 'div',
36359                             cls : 'roo-document-slider-prev',
36360                             cn : [
36361                                 {
36362                                     tag : 'i',
36363                                     cls : 'fa fa-chevron-left'
36364                                 }
36365                             ]
36366                         },
36367                         {
36368                             tag : 'div',
36369                             cls : 'roo-document-slider-thumb',
36370                             cn : [
36371                                 {
36372                                     tag : 'img',
36373                                     cls : 'roo-document-slider-image'
36374                                 }
36375                             ]
36376                         },
36377                         {
36378                             tag : 'div',
36379                             cls : 'roo-document-slider-next',
36380                             cn : [
36381                                 {
36382                                     tag : 'i',
36383                                     cls : 'fa fa-chevron-right'
36384                                 }
36385                             ]
36386                         }
36387                     ]
36388                 }
36389             ]
36390         };
36391         
36392         return cfg;
36393     },
36394     
36395     initEvents : function()
36396     {
36397         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36398         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36399         
36400         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36401         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36402         
36403         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36404         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36405         
36406         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36407         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36408         
36409         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36410         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36411         
36412         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36413         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36414         
36415         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36416         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36417         
36418         this.thumbEl.on('click', this.onClick, this);
36419         
36420         this.prevIndicator.on('click', this.prev, this);
36421         
36422         this.nextIndicator.on('click', this.next, this);
36423         
36424     },
36425     
36426     initial : function()
36427     {
36428         if(this.files.length){
36429             this.indicator = 1;
36430             this.update()
36431         }
36432         
36433         this.fireEvent('initial', this);
36434     },
36435     
36436     update : function()
36437     {
36438         this.imageEl.attr('src', this.files[this.indicator - 1]);
36439         
36440         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36441         
36442         this.prevIndicator.show();
36443         
36444         if(this.indicator == 1){
36445             this.prevIndicator.hide();
36446         }
36447         
36448         this.nextIndicator.show();
36449         
36450         if(this.indicator == this.files.length){
36451             this.nextIndicator.hide();
36452         }
36453         
36454         this.thumbEl.scrollTo('top');
36455         
36456         this.fireEvent('update', this);
36457     },
36458     
36459     onClick : function(e)
36460     {
36461         e.preventDefault();
36462         
36463         this.fireEvent('click', this);
36464     },
36465     
36466     prev : function(e)
36467     {
36468         e.preventDefault();
36469         
36470         this.indicator = Math.max(1, this.indicator - 1);
36471         
36472         this.update();
36473     },
36474     
36475     next : function(e)
36476     {
36477         e.preventDefault();
36478         
36479         this.indicator = Math.min(this.files.length, this.indicator + 1);
36480         
36481         this.update();
36482     }
36483 });
36484 /*
36485  * - LGPL
36486  *
36487  * RadioSet
36488  *
36489  *
36490  */
36491
36492 /**
36493  * @class Roo.bootstrap.RadioSet
36494  * @extends Roo.bootstrap.Input
36495  * Bootstrap RadioSet class
36496  * @cfg {String} indicatorpos (left|right) default left
36497  * @cfg {Boolean} inline (true|false) inline the element (default true)
36498  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36499  * @constructor
36500  * Create a new RadioSet
36501  * @param {Object} config The config object
36502  */
36503
36504 Roo.bootstrap.RadioSet = function(config){
36505     
36506     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36507     
36508     this.radioes = [];
36509     
36510     Roo.bootstrap.RadioSet.register(this);
36511     
36512     this.addEvents({
36513         /**
36514         * @event check
36515         * Fires when the element is checked or unchecked.
36516         * @param {Roo.bootstrap.RadioSet} this This radio
36517         * @param {Roo.bootstrap.Radio} item The checked item
36518         */
36519        check : true,
36520        /**
36521         * @event click
36522         * Fires when the element is click.
36523         * @param {Roo.bootstrap.RadioSet} this This radio set
36524         * @param {Roo.bootstrap.Radio} item The checked item
36525         * @param {Roo.EventObject} e The event object
36526         */
36527        click : true
36528     });
36529     
36530 };
36531
36532 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36533
36534     radioes : false,
36535     
36536     inline : true,
36537     
36538     weight : '',
36539     
36540     indicatorpos : 'left',
36541     
36542     getAutoCreate : function()
36543     {
36544         var label = {
36545             tag : 'label',
36546             cls : 'roo-radio-set-label',
36547             cn : [
36548                 {
36549                     tag : 'span',
36550                     html : this.fieldLabel
36551                 }
36552             ]
36553         };
36554         if (Roo.bootstrap.version == 3) {
36555             
36556             
36557             if(this.indicatorpos == 'left'){
36558                 label.cn.unshift({
36559                     tag : 'i',
36560                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36561                     tooltip : 'This field is required'
36562                 });
36563             } else {
36564                 label.cn.push({
36565                     tag : 'i',
36566                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36567                     tooltip : 'This field is required'
36568                 });
36569             }
36570         }
36571         var items = {
36572             tag : 'div',
36573             cls : 'roo-radio-set-items'
36574         };
36575         
36576         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36577         
36578         if (align === 'left' && this.fieldLabel.length) {
36579             
36580             items = {
36581                 cls : "roo-radio-set-right", 
36582                 cn: [
36583                     items
36584                 ]
36585             };
36586             
36587             if(this.labelWidth > 12){
36588                 label.style = "width: " + this.labelWidth + 'px';
36589             }
36590             
36591             if(this.labelWidth < 13 && this.labelmd == 0){
36592                 this.labelmd = this.labelWidth;
36593             }
36594             
36595             if(this.labellg > 0){
36596                 label.cls += ' col-lg-' + this.labellg;
36597                 items.cls += ' col-lg-' + (12 - this.labellg);
36598             }
36599             
36600             if(this.labelmd > 0){
36601                 label.cls += ' col-md-' + this.labelmd;
36602                 items.cls += ' col-md-' + (12 - this.labelmd);
36603             }
36604             
36605             if(this.labelsm > 0){
36606                 label.cls += ' col-sm-' + this.labelsm;
36607                 items.cls += ' col-sm-' + (12 - this.labelsm);
36608             }
36609             
36610             if(this.labelxs > 0){
36611                 label.cls += ' col-xs-' + this.labelxs;
36612                 items.cls += ' col-xs-' + (12 - this.labelxs);
36613             }
36614         }
36615         
36616         var cfg = {
36617             tag : 'div',
36618             cls : 'roo-radio-set',
36619             cn : [
36620                 {
36621                     tag : 'input',
36622                     cls : 'roo-radio-set-input',
36623                     type : 'hidden',
36624                     name : this.name,
36625                     value : this.value ? this.value :  ''
36626                 },
36627                 label,
36628                 items
36629             ]
36630         };
36631         
36632         if(this.weight.length){
36633             cfg.cls += ' roo-radio-' + this.weight;
36634         }
36635         
36636         if(this.inline) {
36637             cfg.cls += ' roo-radio-set-inline';
36638         }
36639         
36640         var settings=this;
36641         ['xs','sm','md','lg'].map(function(size){
36642             if (settings[size]) {
36643                 cfg.cls += ' col-' + size + '-' + settings[size];
36644             }
36645         });
36646         
36647         return cfg;
36648         
36649     },
36650
36651     initEvents : function()
36652     {
36653         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36654         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36655         
36656         if(!this.fieldLabel.length){
36657             this.labelEl.hide();
36658         }
36659         
36660         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36661         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36662         
36663         this.indicator = this.indicatorEl();
36664         
36665         if(this.indicator){
36666             this.indicator.addClass('invisible');
36667         }
36668         
36669         this.originalValue = this.getValue();
36670         
36671     },
36672     
36673     inputEl: function ()
36674     {
36675         return this.el.select('.roo-radio-set-input', true).first();
36676     },
36677     
36678     getChildContainer : function()
36679     {
36680         return this.itemsEl;
36681     },
36682     
36683     register : function(item)
36684     {
36685         this.radioes.push(item);
36686         
36687     },
36688     
36689     validate : function()
36690     {   
36691         if(this.getVisibilityEl().hasClass('hidden')){
36692             return true;
36693         }
36694         
36695         var valid = false;
36696         
36697         Roo.each(this.radioes, function(i){
36698             if(!i.checked){
36699                 return;
36700             }
36701             
36702             valid = true;
36703             return false;
36704         });
36705         
36706         if(this.allowBlank) {
36707             return true;
36708         }
36709         
36710         if(this.disabled || valid){
36711             this.markValid();
36712             return true;
36713         }
36714         
36715         this.markInvalid();
36716         return false;
36717         
36718     },
36719     
36720     markValid : function()
36721     {
36722         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36723             this.indicatorEl().removeClass('visible');
36724             this.indicatorEl().addClass('invisible');
36725         }
36726         
36727         
36728         if (Roo.bootstrap.version == 3) {
36729             this.el.removeClass([this.invalidClass, this.validClass]);
36730             this.el.addClass(this.validClass);
36731         } else {
36732             this.el.removeClass(['is-invalid','is-valid']);
36733             this.el.addClass(['is-valid']);
36734         }
36735         this.fireEvent('valid', this);
36736     },
36737     
36738     markInvalid : function(msg)
36739     {
36740         if(this.allowBlank || this.disabled){
36741             return;
36742         }
36743         
36744         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36745             this.indicatorEl().removeClass('invisible');
36746             this.indicatorEl().addClass('visible');
36747         }
36748         if (Roo.bootstrap.version == 3) {
36749             this.el.removeClass([this.invalidClass, this.validClass]);
36750             this.el.addClass(this.invalidClass);
36751         } else {
36752             this.el.removeClass(['is-invalid','is-valid']);
36753             this.el.addClass(['is-invalid']);
36754         }
36755         
36756         this.fireEvent('invalid', this, msg);
36757         
36758     },
36759     
36760     setValue : function(v, suppressEvent)
36761     {   
36762         if(this.value === v){
36763             return;
36764         }
36765         
36766         this.value = v;
36767         
36768         if(this.rendered){
36769             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36770         }
36771         
36772         Roo.each(this.radioes, function(i){
36773             i.checked = false;
36774             i.el.removeClass('checked');
36775         });
36776         
36777         Roo.each(this.radioes, function(i){
36778             
36779             if(i.value === v || i.value.toString() === v.toString()){
36780                 i.checked = true;
36781                 i.el.addClass('checked');
36782                 
36783                 if(suppressEvent !== true){
36784                     this.fireEvent('check', this, i);
36785                 }
36786                 
36787                 return false;
36788             }
36789             
36790         }, this);
36791         
36792         this.validate();
36793     },
36794     
36795     clearInvalid : function(){
36796         
36797         if(!this.el || this.preventMark){
36798             return;
36799         }
36800         
36801         this.el.removeClass([this.invalidClass]);
36802         
36803         this.fireEvent('valid', this);
36804     }
36805     
36806 });
36807
36808 Roo.apply(Roo.bootstrap.RadioSet, {
36809     
36810     groups: {},
36811     
36812     register : function(set)
36813     {
36814         this.groups[set.name] = set;
36815     },
36816     
36817     get: function(name) 
36818     {
36819         if (typeof(this.groups[name]) == 'undefined') {
36820             return false;
36821         }
36822         
36823         return this.groups[name] ;
36824     }
36825     
36826 });
36827 /*
36828  * Based on:
36829  * Ext JS Library 1.1.1
36830  * Copyright(c) 2006-2007, Ext JS, LLC.
36831  *
36832  * Originally Released Under LGPL - original licence link has changed is not relivant.
36833  *
36834  * Fork - LGPL
36835  * <script type="text/javascript">
36836  */
36837
36838
36839 /**
36840  * @class Roo.bootstrap.SplitBar
36841  * @extends Roo.util.Observable
36842  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36843  * <br><br>
36844  * Usage:
36845  * <pre><code>
36846 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36847                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36848 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36849 split.minSize = 100;
36850 split.maxSize = 600;
36851 split.animate = true;
36852 split.on('moved', splitterMoved);
36853 </code></pre>
36854  * @constructor
36855  * Create a new SplitBar
36856  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36857  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36858  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36859  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36860                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36861                         position of the SplitBar).
36862  */
36863 Roo.bootstrap.SplitBar = function(cfg){
36864     
36865     /** @private */
36866     
36867     //{
36868     //  dragElement : elm
36869     //  resizingElement: el,
36870         // optional..
36871     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36872     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36873         // existingProxy ???
36874     //}
36875     
36876     this.el = Roo.get(cfg.dragElement, true);
36877     this.el.dom.unselectable = "on";
36878     /** @private */
36879     this.resizingEl = Roo.get(cfg.resizingElement, true);
36880
36881     /**
36882      * @private
36883      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36884      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36885      * @type Number
36886      */
36887     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36888     
36889     /**
36890      * The minimum size of the resizing element. (Defaults to 0)
36891      * @type Number
36892      */
36893     this.minSize = 0;
36894     
36895     /**
36896      * The maximum size of the resizing element. (Defaults to 2000)
36897      * @type Number
36898      */
36899     this.maxSize = 2000;
36900     
36901     /**
36902      * Whether to animate the transition to the new size
36903      * @type Boolean
36904      */
36905     this.animate = false;
36906     
36907     /**
36908      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36909      * @type Boolean
36910      */
36911     this.useShim = false;
36912     
36913     /** @private */
36914     this.shim = null;
36915     
36916     if(!cfg.existingProxy){
36917         /** @private */
36918         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36919     }else{
36920         this.proxy = Roo.get(cfg.existingProxy).dom;
36921     }
36922     /** @private */
36923     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36924     
36925     /** @private */
36926     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36927     
36928     /** @private */
36929     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36930     
36931     /** @private */
36932     this.dragSpecs = {};
36933     
36934     /**
36935      * @private The adapter to use to positon and resize elements
36936      */
36937     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36938     this.adapter.init(this);
36939     
36940     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36941         /** @private */
36942         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36943         this.el.addClass("roo-splitbar-h");
36944     }else{
36945         /** @private */
36946         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36947         this.el.addClass("roo-splitbar-v");
36948     }
36949     
36950     this.addEvents({
36951         /**
36952          * @event resize
36953          * Fires when the splitter is moved (alias for {@link #event-moved})
36954          * @param {Roo.bootstrap.SplitBar} this
36955          * @param {Number} newSize the new width or height
36956          */
36957         "resize" : true,
36958         /**
36959          * @event moved
36960          * Fires when the splitter is moved
36961          * @param {Roo.bootstrap.SplitBar} this
36962          * @param {Number} newSize the new width or height
36963          */
36964         "moved" : true,
36965         /**
36966          * @event beforeresize
36967          * Fires before the splitter is dragged
36968          * @param {Roo.bootstrap.SplitBar} this
36969          */
36970         "beforeresize" : true,
36971
36972         "beforeapply" : true
36973     });
36974
36975     Roo.util.Observable.call(this);
36976 };
36977
36978 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36979     onStartProxyDrag : function(x, y){
36980         this.fireEvent("beforeresize", this);
36981         if(!this.overlay){
36982             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36983             o.unselectable();
36984             o.enableDisplayMode("block");
36985             // all splitbars share the same overlay
36986             Roo.bootstrap.SplitBar.prototype.overlay = o;
36987         }
36988         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36989         this.overlay.show();
36990         Roo.get(this.proxy).setDisplayed("block");
36991         var size = this.adapter.getElementSize(this);
36992         this.activeMinSize = this.getMinimumSize();;
36993         this.activeMaxSize = this.getMaximumSize();;
36994         var c1 = size - this.activeMinSize;
36995         var c2 = Math.max(this.activeMaxSize - size, 0);
36996         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36997             this.dd.resetConstraints();
36998             this.dd.setXConstraint(
36999                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37000                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37001             );
37002             this.dd.setYConstraint(0, 0);
37003         }else{
37004             this.dd.resetConstraints();
37005             this.dd.setXConstraint(0, 0);
37006             this.dd.setYConstraint(
37007                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37008                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37009             );
37010          }
37011         this.dragSpecs.startSize = size;
37012         this.dragSpecs.startPoint = [x, y];
37013         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37014     },
37015     
37016     /** 
37017      * @private Called after the drag operation by the DDProxy
37018      */
37019     onEndProxyDrag : function(e){
37020         Roo.get(this.proxy).setDisplayed(false);
37021         var endPoint = Roo.lib.Event.getXY(e);
37022         if(this.overlay){
37023             this.overlay.hide();
37024         }
37025         var newSize;
37026         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37027             newSize = this.dragSpecs.startSize + 
37028                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37029                     endPoint[0] - this.dragSpecs.startPoint[0] :
37030                     this.dragSpecs.startPoint[0] - endPoint[0]
37031                 );
37032         }else{
37033             newSize = this.dragSpecs.startSize + 
37034                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37035                     endPoint[1] - this.dragSpecs.startPoint[1] :
37036                     this.dragSpecs.startPoint[1] - endPoint[1]
37037                 );
37038         }
37039         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37040         if(newSize != this.dragSpecs.startSize){
37041             if(this.fireEvent('beforeapply', this, newSize) !== false){
37042                 this.adapter.setElementSize(this, newSize);
37043                 this.fireEvent("moved", this, newSize);
37044                 this.fireEvent("resize", this, newSize);
37045             }
37046         }
37047     },
37048     
37049     /**
37050      * Get the adapter this SplitBar uses
37051      * @return The adapter object
37052      */
37053     getAdapter : function(){
37054         return this.adapter;
37055     },
37056     
37057     /**
37058      * Set the adapter this SplitBar uses
37059      * @param {Object} adapter A SplitBar adapter object
37060      */
37061     setAdapter : function(adapter){
37062         this.adapter = adapter;
37063         this.adapter.init(this);
37064     },
37065     
37066     /**
37067      * Gets the minimum size for the resizing element
37068      * @return {Number} The minimum size
37069      */
37070     getMinimumSize : function(){
37071         return this.minSize;
37072     },
37073     
37074     /**
37075      * Sets the minimum size for the resizing element
37076      * @param {Number} minSize The minimum size
37077      */
37078     setMinimumSize : function(minSize){
37079         this.minSize = minSize;
37080     },
37081     
37082     /**
37083      * Gets the maximum size for the resizing element
37084      * @return {Number} The maximum size
37085      */
37086     getMaximumSize : function(){
37087         return this.maxSize;
37088     },
37089     
37090     /**
37091      * Sets the maximum size for the resizing element
37092      * @param {Number} maxSize The maximum size
37093      */
37094     setMaximumSize : function(maxSize){
37095         this.maxSize = maxSize;
37096     },
37097     
37098     /**
37099      * Sets the initialize size for the resizing element
37100      * @param {Number} size The initial size
37101      */
37102     setCurrentSize : function(size){
37103         var oldAnimate = this.animate;
37104         this.animate = false;
37105         this.adapter.setElementSize(this, size);
37106         this.animate = oldAnimate;
37107     },
37108     
37109     /**
37110      * Destroy this splitbar. 
37111      * @param {Boolean} removeEl True to remove the element
37112      */
37113     destroy : function(removeEl){
37114         if(this.shim){
37115             this.shim.remove();
37116         }
37117         this.dd.unreg();
37118         this.proxy.parentNode.removeChild(this.proxy);
37119         if(removeEl){
37120             this.el.remove();
37121         }
37122     }
37123 });
37124
37125 /**
37126  * @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.
37127  */
37128 Roo.bootstrap.SplitBar.createProxy = function(dir){
37129     var proxy = new Roo.Element(document.createElement("div"));
37130     proxy.unselectable();
37131     var cls = 'roo-splitbar-proxy';
37132     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37133     document.body.appendChild(proxy.dom);
37134     return proxy.dom;
37135 };
37136
37137 /** 
37138  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37139  * Default Adapter. It assumes the splitter and resizing element are not positioned
37140  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37141  */
37142 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37143 };
37144
37145 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37146     // do nothing for now
37147     init : function(s){
37148     
37149     },
37150     /**
37151      * Called before drag operations to get the current size of the resizing element. 
37152      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37153      */
37154      getElementSize : function(s){
37155         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37156             return s.resizingEl.getWidth();
37157         }else{
37158             return s.resizingEl.getHeight();
37159         }
37160     },
37161     
37162     /**
37163      * Called after drag operations to set the size of the resizing element.
37164      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37165      * @param {Number} newSize The new size to set
37166      * @param {Function} onComplete A function to be invoked when resizing is complete
37167      */
37168     setElementSize : function(s, newSize, onComplete){
37169         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37170             if(!s.animate){
37171                 s.resizingEl.setWidth(newSize);
37172                 if(onComplete){
37173                     onComplete(s, newSize);
37174                 }
37175             }else{
37176                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37177             }
37178         }else{
37179             
37180             if(!s.animate){
37181                 s.resizingEl.setHeight(newSize);
37182                 if(onComplete){
37183                     onComplete(s, newSize);
37184                 }
37185             }else{
37186                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37187             }
37188         }
37189     }
37190 };
37191
37192 /** 
37193  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37194  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37195  * Adapter that  moves the splitter element to align with the resized sizing element. 
37196  * Used with an absolute positioned SplitBar.
37197  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37198  * document.body, make sure you assign an id to the body element.
37199  */
37200 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37201     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37202     this.container = Roo.get(container);
37203 };
37204
37205 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37206     init : function(s){
37207         this.basic.init(s);
37208     },
37209     
37210     getElementSize : function(s){
37211         return this.basic.getElementSize(s);
37212     },
37213     
37214     setElementSize : function(s, newSize, onComplete){
37215         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37216     },
37217     
37218     moveSplitter : function(s){
37219         var yes = Roo.bootstrap.SplitBar;
37220         switch(s.placement){
37221             case yes.LEFT:
37222                 s.el.setX(s.resizingEl.getRight());
37223                 break;
37224             case yes.RIGHT:
37225                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37226                 break;
37227             case yes.TOP:
37228                 s.el.setY(s.resizingEl.getBottom());
37229                 break;
37230             case yes.BOTTOM:
37231                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37232                 break;
37233         }
37234     }
37235 };
37236
37237 /**
37238  * Orientation constant - Create a vertical SplitBar
37239  * @static
37240  * @type Number
37241  */
37242 Roo.bootstrap.SplitBar.VERTICAL = 1;
37243
37244 /**
37245  * Orientation constant - Create a horizontal SplitBar
37246  * @static
37247  * @type Number
37248  */
37249 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37250
37251 /**
37252  * Placement constant - The resizing element is to the left of the splitter element
37253  * @static
37254  * @type Number
37255  */
37256 Roo.bootstrap.SplitBar.LEFT = 1;
37257
37258 /**
37259  * Placement constant - The resizing element is to the right of the splitter element
37260  * @static
37261  * @type Number
37262  */
37263 Roo.bootstrap.SplitBar.RIGHT = 2;
37264
37265 /**
37266  * Placement constant - The resizing element is positioned above the splitter element
37267  * @static
37268  * @type Number
37269  */
37270 Roo.bootstrap.SplitBar.TOP = 3;
37271
37272 /**
37273  * Placement constant - The resizing element is positioned under splitter element
37274  * @static
37275  * @type Number
37276  */
37277 Roo.bootstrap.SplitBar.BOTTOM = 4;
37278 Roo.namespace("Roo.bootstrap.layout");/*
37279  * Based on:
37280  * Ext JS Library 1.1.1
37281  * Copyright(c) 2006-2007, Ext JS, LLC.
37282  *
37283  * Originally Released Under LGPL - original licence link has changed is not relivant.
37284  *
37285  * Fork - LGPL
37286  * <script type="text/javascript">
37287  */
37288
37289 /**
37290  * @class Roo.bootstrap.layout.Manager
37291  * @extends Roo.bootstrap.Component
37292  * Base class for layout managers.
37293  */
37294 Roo.bootstrap.layout.Manager = function(config)
37295 {
37296     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37297
37298
37299
37300
37301
37302     /** false to disable window resize monitoring @type Boolean */
37303     this.monitorWindowResize = true;
37304     this.regions = {};
37305     this.addEvents({
37306         /**
37307          * @event layout
37308          * Fires when a layout is performed.
37309          * @param {Roo.LayoutManager} this
37310          */
37311         "layout" : true,
37312         /**
37313          * @event regionresized
37314          * Fires when the user resizes a region.
37315          * @param {Roo.LayoutRegion} region The resized region
37316          * @param {Number} newSize The new size (width for east/west, height for north/south)
37317          */
37318         "regionresized" : true,
37319         /**
37320          * @event regioncollapsed
37321          * Fires when a region is collapsed.
37322          * @param {Roo.LayoutRegion} region The collapsed region
37323          */
37324         "regioncollapsed" : true,
37325         /**
37326          * @event regionexpanded
37327          * Fires when a region is expanded.
37328          * @param {Roo.LayoutRegion} region The expanded region
37329          */
37330         "regionexpanded" : true
37331     });
37332     this.updating = false;
37333
37334     if (config.el) {
37335         this.el = Roo.get(config.el);
37336         this.initEvents();
37337     }
37338
37339 };
37340
37341 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37342
37343
37344     regions : null,
37345
37346     monitorWindowResize : true,
37347
37348
37349     updating : false,
37350
37351
37352     onRender : function(ct, position)
37353     {
37354         if(!this.el){
37355             this.el = Roo.get(ct);
37356             this.initEvents();
37357         }
37358         //this.fireEvent('render',this);
37359     },
37360
37361
37362     initEvents: function()
37363     {
37364
37365
37366         // ie scrollbar fix
37367         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37368             document.body.scroll = "no";
37369         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37370             this.el.position('relative');
37371         }
37372         this.id = this.el.id;
37373         this.el.addClass("roo-layout-container");
37374         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37375         if(this.el.dom != document.body ) {
37376             this.el.on('resize', this.layout,this);
37377             this.el.on('show', this.layout,this);
37378         }
37379
37380     },
37381
37382     /**
37383      * Returns true if this layout is currently being updated
37384      * @return {Boolean}
37385      */
37386     isUpdating : function(){
37387         return this.updating;
37388     },
37389
37390     /**
37391      * Suspend the LayoutManager from doing auto-layouts while
37392      * making multiple add or remove calls
37393      */
37394     beginUpdate : function(){
37395         this.updating = true;
37396     },
37397
37398     /**
37399      * Restore auto-layouts and optionally disable the manager from performing a layout
37400      * @param {Boolean} noLayout true to disable a layout update
37401      */
37402     endUpdate : function(noLayout){
37403         this.updating = false;
37404         if(!noLayout){
37405             this.layout();
37406         }
37407     },
37408
37409     layout: function(){
37410         // abstract...
37411     },
37412
37413     onRegionResized : function(region, newSize){
37414         this.fireEvent("regionresized", region, newSize);
37415         this.layout();
37416     },
37417
37418     onRegionCollapsed : function(region){
37419         this.fireEvent("regioncollapsed", region);
37420     },
37421
37422     onRegionExpanded : function(region){
37423         this.fireEvent("regionexpanded", region);
37424     },
37425
37426     /**
37427      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37428      * performs box-model adjustments.
37429      * @return {Object} The size as an object {width: (the width), height: (the height)}
37430      */
37431     getViewSize : function()
37432     {
37433         var size;
37434         if(this.el.dom != document.body){
37435             size = this.el.getSize();
37436         }else{
37437             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37438         }
37439         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37440         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37441         return size;
37442     },
37443
37444     /**
37445      * Returns the Element this layout is bound to.
37446      * @return {Roo.Element}
37447      */
37448     getEl : function(){
37449         return this.el;
37450     },
37451
37452     /**
37453      * Returns the specified region.
37454      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37455      * @return {Roo.LayoutRegion}
37456      */
37457     getRegion : function(target){
37458         return this.regions[target.toLowerCase()];
37459     },
37460
37461     onWindowResize : function(){
37462         if(this.monitorWindowResize){
37463             this.layout();
37464         }
37465     }
37466 });
37467 /*
37468  * Based on:
37469  * Ext JS Library 1.1.1
37470  * Copyright(c) 2006-2007, Ext JS, LLC.
37471  *
37472  * Originally Released Under LGPL - original licence link has changed is not relivant.
37473  *
37474  * Fork - LGPL
37475  * <script type="text/javascript">
37476  */
37477 /**
37478  * @class Roo.bootstrap.layout.Border
37479  * @extends Roo.bootstrap.layout.Manager
37480  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37481  * please see: examples/bootstrap/nested.html<br><br>
37482  
37483 <b>The container the layout is rendered into can be either the body element or any other element.
37484 If it is not the body element, the container needs to either be an absolute positioned element,
37485 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37486 the container size if it is not the body element.</b>
37487
37488 * @constructor
37489 * Create a new Border
37490 * @param {Object} config Configuration options
37491  */
37492 Roo.bootstrap.layout.Border = function(config){
37493     config = config || {};
37494     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37495     
37496     
37497     
37498     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37499         if(config[region]){
37500             config[region].region = region;
37501             this.addRegion(config[region]);
37502         }
37503     },this);
37504     
37505 };
37506
37507 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37508
37509 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37510     
37511     parent : false, // this might point to a 'nest' or a ???
37512     
37513     /**
37514      * Creates and adds a new region if it doesn't already exist.
37515      * @param {String} target The target region key (north, south, east, west or center).
37516      * @param {Object} config The regions config object
37517      * @return {BorderLayoutRegion} The new region
37518      */
37519     addRegion : function(config)
37520     {
37521         if(!this.regions[config.region]){
37522             var r = this.factory(config);
37523             this.bindRegion(r);
37524         }
37525         return this.regions[config.region];
37526     },
37527
37528     // private (kinda)
37529     bindRegion : function(r){
37530         this.regions[r.config.region] = r;
37531         
37532         r.on("visibilitychange",    this.layout, this);
37533         r.on("paneladded",          this.layout, this);
37534         r.on("panelremoved",        this.layout, this);
37535         r.on("invalidated",         this.layout, this);
37536         r.on("resized",             this.onRegionResized, this);
37537         r.on("collapsed",           this.onRegionCollapsed, this);
37538         r.on("expanded",            this.onRegionExpanded, this);
37539     },
37540
37541     /**
37542      * Performs a layout update.
37543      */
37544     layout : function()
37545     {
37546         if(this.updating) {
37547             return;
37548         }
37549         
37550         // render all the rebions if they have not been done alreayd?
37551         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37552             if(this.regions[region] && !this.regions[region].bodyEl){
37553                 this.regions[region].onRender(this.el)
37554             }
37555         },this);
37556         
37557         var size = this.getViewSize();
37558         var w = size.width;
37559         var h = size.height;
37560         var centerW = w;
37561         var centerH = h;
37562         var centerY = 0;
37563         var centerX = 0;
37564         //var x = 0, y = 0;
37565
37566         var rs = this.regions;
37567         var north = rs["north"];
37568         var south = rs["south"]; 
37569         var west = rs["west"];
37570         var east = rs["east"];
37571         var center = rs["center"];
37572         //if(this.hideOnLayout){ // not supported anymore
37573             //c.el.setStyle("display", "none");
37574         //}
37575         if(north && north.isVisible()){
37576             var b = north.getBox();
37577             var m = north.getMargins();
37578             b.width = w - (m.left+m.right);
37579             b.x = m.left;
37580             b.y = m.top;
37581             centerY = b.height + b.y + m.bottom;
37582             centerH -= centerY;
37583             north.updateBox(this.safeBox(b));
37584         }
37585         if(south && south.isVisible()){
37586             var b = south.getBox();
37587             var m = south.getMargins();
37588             b.width = w - (m.left+m.right);
37589             b.x = m.left;
37590             var totalHeight = (b.height + m.top + m.bottom);
37591             b.y = h - totalHeight + m.top;
37592             centerH -= totalHeight;
37593             south.updateBox(this.safeBox(b));
37594         }
37595         if(west && west.isVisible()){
37596             var b = west.getBox();
37597             var m = west.getMargins();
37598             b.height = centerH - (m.top+m.bottom);
37599             b.x = m.left;
37600             b.y = centerY + m.top;
37601             var totalWidth = (b.width + m.left + m.right);
37602             centerX += totalWidth;
37603             centerW -= totalWidth;
37604             west.updateBox(this.safeBox(b));
37605         }
37606         if(east && east.isVisible()){
37607             var b = east.getBox();
37608             var m = east.getMargins();
37609             b.height = centerH - (m.top+m.bottom);
37610             var totalWidth = (b.width + m.left + m.right);
37611             b.x = w - totalWidth + m.left;
37612             b.y = centerY + m.top;
37613             centerW -= totalWidth;
37614             east.updateBox(this.safeBox(b));
37615         }
37616         if(center){
37617             var m = center.getMargins();
37618             var centerBox = {
37619                 x: centerX + m.left,
37620                 y: centerY + m.top,
37621                 width: centerW - (m.left+m.right),
37622                 height: centerH - (m.top+m.bottom)
37623             };
37624             //if(this.hideOnLayout){
37625                 //center.el.setStyle("display", "block");
37626             //}
37627             center.updateBox(this.safeBox(centerBox));
37628         }
37629         this.el.repaint();
37630         this.fireEvent("layout", this);
37631     },
37632
37633     // private
37634     safeBox : function(box){
37635         box.width = Math.max(0, box.width);
37636         box.height = Math.max(0, box.height);
37637         return box;
37638     },
37639
37640     /**
37641      * Adds a ContentPanel (or subclass) to this layout.
37642      * @param {String} target The target region key (north, south, east, west or center).
37643      * @param {Roo.ContentPanel} panel The panel to add
37644      * @return {Roo.ContentPanel} The added panel
37645      */
37646     add : function(target, panel){
37647          
37648         target = target.toLowerCase();
37649         return this.regions[target].add(panel);
37650     },
37651
37652     /**
37653      * Remove a ContentPanel (or subclass) to this layout.
37654      * @param {String} target The target region key (north, south, east, west or center).
37655      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37656      * @return {Roo.ContentPanel} The removed panel
37657      */
37658     remove : function(target, panel){
37659         target = target.toLowerCase();
37660         return this.regions[target].remove(panel);
37661     },
37662
37663     /**
37664      * Searches all regions for a panel with the specified id
37665      * @param {String} panelId
37666      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37667      */
37668     findPanel : function(panelId){
37669         var rs = this.regions;
37670         for(var target in rs){
37671             if(typeof rs[target] != "function"){
37672                 var p = rs[target].getPanel(panelId);
37673                 if(p){
37674                     return p;
37675                 }
37676             }
37677         }
37678         return null;
37679     },
37680
37681     /**
37682      * Searches all regions for a panel with the specified id and activates (shows) it.
37683      * @param {String/ContentPanel} panelId The panels id or the panel itself
37684      * @return {Roo.ContentPanel} The shown panel or null
37685      */
37686     showPanel : function(panelId) {
37687       var rs = this.regions;
37688       for(var target in rs){
37689          var r = rs[target];
37690          if(typeof r != "function"){
37691             if(r.hasPanel(panelId)){
37692                return r.showPanel(panelId);
37693             }
37694          }
37695       }
37696       return null;
37697    },
37698
37699    /**
37700      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37701      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37702      */
37703    /*
37704     restoreState : function(provider){
37705         if(!provider){
37706             provider = Roo.state.Manager;
37707         }
37708         var sm = new Roo.LayoutStateManager();
37709         sm.init(this, provider);
37710     },
37711 */
37712  
37713  
37714     /**
37715      * Adds a xtype elements to the layout.
37716      * <pre><code>
37717
37718 layout.addxtype({
37719        xtype : 'ContentPanel',
37720        region: 'west',
37721        items: [ .... ]
37722    }
37723 );
37724
37725 layout.addxtype({
37726         xtype : 'NestedLayoutPanel',
37727         region: 'west',
37728         layout: {
37729            center: { },
37730            west: { }   
37731         },
37732         items : [ ... list of content panels or nested layout panels.. ]
37733    }
37734 );
37735 </code></pre>
37736      * @param {Object} cfg Xtype definition of item to add.
37737      */
37738     addxtype : function(cfg)
37739     {
37740         // basically accepts a pannel...
37741         // can accept a layout region..!?!?
37742         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37743         
37744         
37745         // theory?  children can only be panels??
37746         
37747         //if (!cfg.xtype.match(/Panel$/)) {
37748         //    return false;
37749         //}
37750         var ret = false;
37751         
37752         if (typeof(cfg.region) == 'undefined') {
37753             Roo.log("Failed to add Panel, region was not set");
37754             Roo.log(cfg);
37755             return false;
37756         }
37757         var region = cfg.region;
37758         delete cfg.region;
37759         
37760           
37761         var xitems = [];
37762         if (cfg.items) {
37763             xitems = cfg.items;
37764             delete cfg.items;
37765         }
37766         var nb = false;
37767         
37768         if ( region == 'center') {
37769             Roo.log("Center: " + cfg.title);
37770         }
37771         
37772         
37773         switch(cfg.xtype) 
37774         {
37775             case 'Content':  // ContentPanel (el, cfg)
37776             case 'Scroll':  // ContentPanel (el, cfg)
37777             case 'View': 
37778                 cfg.autoCreate = cfg.autoCreate || true;
37779                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37780                 //} else {
37781                 //    var el = this.el.createChild();
37782                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37783                 //}
37784                 
37785                 this.add(region, ret);
37786                 break;
37787             
37788             /*
37789             case 'TreePanel': // our new panel!
37790                 cfg.el = this.el.createChild();
37791                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37792                 this.add(region, ret);
37793                 break;
37794             */
37795             
37796             case 'Nest': 
37797                 // create a new Layout (which is  a Border Layout...
37798                 
37799                 var clayout = cfg.layout;
37800                 clayout.el  = this.el.createChild();
37801                 clayout.items   = clayout.items  || [];
37802                 
37803                 delete cfg.layout;
37804                 
37805                 // replace this exitems with the clayout ones..
37806                 xitems = clayout.items;
37807                  
37808                 // force background off if it's in center...
37809                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37810                     cfg.background = false;
37811                 }
37812                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37813                 
37814                 
37815                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37816                 //console.log('adding nested layout panel '  + cfg.toSource());
37817                 this.add(region, ret);
37818                 nb = {}; /// find first...
37819                 break;
37820             
37821             case 'Grid':
37822                 
37823                 // needs grid and region
37824                 
37825                 //var el = this.getRegion(region).el.createChild();
37826                 /*
37827                  *var el = this.el.createChild();
37828                 // create the grid first...
37829                 cfg.grid.container = el;
37830                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37831                 */
37832                 
37833                 if (region == 'center' && this.active ) {
37834                     cfg.background = false;
37835                 }
37836                 
37837                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37838                 
37839                 this.add(region, ret);
37840                 /*
37841                 if (cfg.background) {
37842                     // render grid on panel activation (if panel background)
37843                     ret.on('activate', function(gp) {
37844                         if (!gp.grid.rendered) {
37845                     //        gp.grid.render(el);
37846                         }
37847                     });
37848                 } else {
37849                   //  cfg.grid.render(el);
37850                 }
37851                 */
37852                 break;
37853            
37854            
37855             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37856                 // it was the old xcomponent building that caused this before.
37857                 // espeically if border is the top element in the tree.
37858                 ret = this;
37859                 break; 
37860                 
37861                     
37862                 
37863                 
37864                 
37865             default:
37866                 /*
37867                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37868                     
37869                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37870                     this.add(region, ret);
37871                 } else {
37872                 */
37873                     Roo.log(cfg);
37874                     throw "Can not add '" + cfg.xtype + "' to Border";
37875                     return null;
37876              
37877                                 
37878              
37879         }
37880         this.beginUpdate();
37881         // add children..
37882         var region = '';
37883         var abn = {};
37884         Roo.each(xitems, function(i)  {
37885             region = nb && i.region ? i.region : false;
37886             
37887             var add = ret.addxtype(i);
37888            
37889             if (region) {
37890                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37891                 if (!i.background) {
37892                     abn[region] = nb[region] ;
37893                 }
37894             }
37895             
37896         });
37897         this.endUpdate();
37898
37899         // make the last non-background panel active..
37900         //if (nb) { Roo.log(abn); }
37901         if (nb) {
37902             
37903             for(var r in abn) {
37904                 region = this.getRegion(r);
37905                 if (region) {
37906                     // tried using nb[r], but it does not work..
37907                      
37908                     region.showPanel(abn[r]);
37909                    
37910                 }
37911             }
37912         }
37913         return ret;
37914         
37915     },
37916     
37917     
37918 // private
37919     factory : function(cfg)
37920     {
37921         
37922         var validRegions = Roo.bootstrap.layout.Border.regions;
37923
37924         var target = cfg.region;
37925         cfg.mgr = this;
37926         
37927         var r = Roo.bootstrap.layout;
37928         Roo.log(target);
37929         switch(target){
37930             case "north":
37931                 return new r.North(cfg);
37932             case "south":
37933                 return new r.South(cfg);
37934             case "east":
37935                 return new r.East(cfg);
37936             case "west":
37937                 return new r.West(cfg);
37938             case "center":
37939                 return new r.Center(cfg);
37940         }
37941         throw 'Layout region "'+target+'" not supported.';
37942     }
37943     
37944     
37945 });
37946  /*
37947  * Based on:
37948  * Ext JS Library 1.1.1
37949  * Copyright(c) 2006-2007, Ext JS, LLC.
37950  *
37951  * Originally Released Under LGPL - original licence link has changed is not relivant.
37952  *
37953  * Fork - LGPL
37954  * <script type="text/javascript">
37955  */
37956  
37957 /**
37958  * @class Roo.bootstrap.layout.Basic
37959  * @extends Roo.util.Observable
37960  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37961  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37962  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37963  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37964  * @cfg {string}   region  the region that it inhabits..
37965  * @cfg {bool}   skipConfig skip config?
37966  * 
37967
37968  */
37969 Roo.bootstrap.layout.Basic = function(config){
37970     
37971     this.mgr = config.mgr;
37972     
37973     this.position = config.region;
37974     
37975     var skipConfig = config.skipConfig;
37976     
37977     this.events = {
37978         /**
37979          * @scope Roo.BasicLayoutRegion
37980          */
37981         
37982         /**
37983          * @event beforeremove
37984          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37985          * @param {Roo.LayoutRegion} this
37986          * @param {Roo.ContentPanel} panel The panel
37987          * @param {Object} e The cancel event object
37988          */
37989         "beforeremove" : true,
37990         /**
37991          * @event invalidated
37992          * Fires when the layout for this region is changed.
37993          * @param {Roo.LayoutRegion} this
37994          */
37995         "invalidated" : true,
37996         /**
37997          * @event visibilitychange
37998          * Fires when this region is shown or hidden 
37999          * @param {Roo.LayoutRegion} this
38000          * @param {Boolean} visibility true or false
38001          */
38002         "visibilitychange" : true,
38003         /**
38004          * @event paneladded
38005          * Fires when a panel is added. 
38006          * @param {Roo.LayoutRegion} this
38007          * @param {Roo.ContentPanel} panel The panel
38008          */
38009         "paneladded" : true,
38010         /**
38011          * @event panelremoved
38012          * Fires when a panel is removed. 
38013          * @param {Roo.LayoutRegion} this
38014          * @param {Roo.ContentPanel} panel The panel
38015          */
38016         "panelremoved" : true,
38017         /**
38018          * @event beforecollapse
38019          * Fires when this region before collapse.
38020          * @param {Roo.LayoutRegion} this
38021          */
38022         "beforecollapse" : true,
38023         /**
38024          * @event collapsed
38025          * Fires when this region is collapsed.
38026          * @param {Roo.LayoutRegion} this
38027          */
38028         "collapsed" : true,
38029         /**
38030          * @event expanded
38031          * Fires when this region is expanded.
38032          * @param {Roo.LayoutRegion} this
38033          */
38034         "expanded" : true,
38035         /**
38036          * @event slideshow
38037          * Fires when this region is slid into view.
38038          * @param {Roo.LayoutRegion} this
38039          */
38040         "slideshow" : true,
38041         /**
38042          * @event slidehide
38043          * Fires when this region slides out of view. 
38044          * @param {Roo.LayoutRegion} this
38045          */
38046         "slidehide" : true,
38047         /**
38048          * @event panelactivated
38049          * Fires when a panel is activated. 
38050          * @param {Roo.LayoutRegion} this
38051          * @param {Roo.ContentPanel} panel The activated panel
38052          */
38053         "panelactivated" : true,
38054         /**
38055          * @event resized
38056          * Fires when the user resizes this region. 
38057          * @param {Roo.LayoutRegion} this
38058          * @param {Number} newSize The new size (width for east/west, height for north/south)
38059          */
38060         "resized" : true
38061     };
38062     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38063     this.panels = new Roo.util.MixedCollection();
38064     this.panels.getKey = this.getPanelId.createDelegate(this);
38065     this.box = null;
38066     this.activePanel = null;
38067     // ensure listeners are added...
38068     
38069     if (config.listeners || config.events) {
38070         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38071             listeners : config.listeners || {},
38072             events : config.events || {}
38073         });
38074     }
38075     
38076     if(skipConfig !== true){
38077         this.applyConfig(config);
38078     }
38079 };
38080
38081 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38082 {
38083     getPanelId : function(p){
38084         return p.getId();
38085     },
38086     
38087     applyConfig : function(config){
38088         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38089         this.config = config;
38090         
38091     },
38092     
38093     /**
38094      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38095      * the width, for horizontal (north, south) the height.
38096      * @param {Number} newSize The new width or height
38097      */
38098     resizeTo : function(newSize){
38099         var el = this.el ? this.el :
38100                  (this.activePanel ? this.activePanel.getEl() : null);
38101         if(el){
38102             switch(this.position){
38103                 case "east":
38104                 case "west":
38105                     el.setWidth(newSize);
38106                     this.fireEvent("resized", this, newSize);
38107                 break;
38108                 case "north":
38109                 case "south":
38110                     el.setHeight(newSize);
38111                     this.fireEvent("resized", this, newSize);
38112                 break;                
38113             }
38114         }
38115     },
38116     
38117     getBox : function(){
38118         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38119     },
38120     
38121     getMargins : function(){
38122         return this.margins;
38123     },
38124     
38125     updateBox : function(box){
38126         this.box = box;
38127         var el = this.activePanel.getEl();
38128         el.dom.style.left = box.x + "px";
38129         el.dom.style.top = box.y + "px";
38130         this.activePanel.setSize(box.width, box.height);
38131     },
38132     
38133     /**
38134      * Returns the container element for this region.
38135      * @return {Roo.Element}
38136      */
38137     getEl : function(){
38138         return this.activePanel;
38139     },
38140     
38141     /**
38142      * Returns true if this region is currently visible.
38143      * @return {Boolean}
38144      */
38145     isVisible : function(){
38146         return this.activePanel ? true : false;
38147     },
38148     
38149     setActivePanel : function(panel){
38150         panel = this.getPanel(panel);
38151         if(this.activePanel && this.activePanel != panel){
38152             this.activePanel.setActiveState(false);
38153             this.activePanel.getEl().setLeftTop(-10000,-10000);
38154         }
38155         this.activePanel = panel;
38156         panel.setActiveState(true);
38157         if(this.box){
38158             panel.setSize(this.box.width, this.box.height);
38159         }
38160         this.fireEvent("panelactivated", this, panel);
38161         this.fireEvent("invalidated");
38162     },
38163     
38164     /**
38165      * Show the specified panel.
38166      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38167      * @return {Roo.ContentPanel} The shown panel or null
38168      */
38169     showPanel : function(panel){
38170         panel = this.getPanel(panel);
38171         if(panel){
38172             this.setActivePanel(panel);
38173         }
38174         return panel;
38175     },
38176     
38177     /**
38178      * Get the active panel for this region.
38179      * @return {Roo.ContentPanel} The active panel or null
38180      */
38181     getActivePanel : function(){
38182         return this.activePanel;
38183     },
38184     
38185     /**
38186      * Add the passed ContentPanel(s)
38187      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38188      * @return {Roo.ContentPanel} The panel added (if only one was added)
38189      */
38190     add : function(panel){
38191         if(arguments.length > 1){
38192             for(var i = 0, len = arguments.length; i < len; i++) {
38193                 this.add(arguments[i]);
38194             }
38195             return null;
38196         }
38197         if(this.hasPanel(panel)){
38198             this.showPanel(panel);
38199             return panel;
38200         }
38201         var el = panel.getEl();
38202         if(el.dom.parentNode != this.mgr.el.dom){
38203             this.mgr.el.dom.appendChild(el.dom);
38204         }
38205         if(panel.setRegion){
38206             panel.setRegion(this);
38207         }
38208         this.panels.add(panel);
38209         el.setStyle("position", "absolute");
38210         if(!panel.background){
38211             this.setActivePanel(panel);
38212             if(this.config.initialSize && this.panels.getCount()==1){
38213                 this.resizeTo(this.config.initialSize);
38214             }
38215         }
38216         this.fireEvent("paneladded", this, panel);
38217         return panel;
38218     },
38219     
38220     /**
38221      * Returns true if the panel is in this region.
38222      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38223      * @return {Boolean}
38224      */
38225     hasPanel : function(panel){
38226         if(typeof panel == "object"){ // must be panel obj
38227             panel = panel.getId();
38228         }
38229         return this.getPanel(panel) ? true : false;
38230     },
38231     
38232     /**
38233      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38234      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38235      * @param {Boolean} preservePanel Overrides the config preservePanel option
38236      * @return {Roo.ContentPanel} The panel that was removed
38237      */
38238     remove : function(panel, preservePanel){
38239         panel = this.getPanel(panel);
38240         if(!panel){
38241             return null;
38242         }
38243         var e = {};
38244         this.fireEvent("beforeremove", this, panel, e);
38245         if(e.cancel === true){
38246             return null;
38247         }
38248         var panelId = panel.getId();
38249         this.panels.removeKey(panelId);
38250         return panel;
38251     },
38252     
38253     /**
38254      * Returns the panel specified or null if it's not in this region.
38255      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38256      * @return {Roo.ContentPanel}
38257      */
38258     getPanel : function(id){
38259         if(typeof id == "object"){ // must be panel obj
38260             return id;
38261         }
38262         return this.panels.get(id);
38263     },
38264     
38265     /**
38266      * Returns this regions position (north/south/east/west/center).
38267      * @return {String} 
38268      */
38269     getPosition: function(){
38270         return this.position;    
38271     }
38272 });/*
38273  * Based on:
38274  * Ext JS Library 1.1.1
38275  * Copyright(c) 2006-2007, Ext JS, LLC.
38276  *
38277  * Originally Released Under LGPL - original licence link has changed is not relivant.
38278  *
38279  * Fork - LGPL
38280  * <script type="text/javascript">
38281  */
38282  
38283 /**
38284  * @class Roo.bootstrap.layout.Region
38285  * @extends Roo.bootstrap.layout.Basic
38286  * This class represents a region in a layout manager.
38287  
38288  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38289  * @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})
38290  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38291  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38292  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38293  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38294  * @cfg {String}    title           The title for the region (overrides panel titles)
38295  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38296  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38297  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38298  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38299  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38300  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38301  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38302  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38303  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38304  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38305
38306  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38307  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38308  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38309  * @cfg {Number}    width           For East/West panels
38310  * @cfg {Number}    height          For North/South panels
38311  * @cfg {Boolean}   split           To show the splitter
38312  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38313  * 
38314  * @cfg {string}   cls             Extra CSS classes to add to region
38315  * 
38316  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38317  * @cfg {string}   region  the region that it inhabits..
38318  *
38319
38320  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38321  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38322
38323  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38324  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38325  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38326  */
38327 Roo.bootstrap.layout.Region = function(config)
38328 {
38329     this.applyConfig(config);
38330
38331     var mgr = config.mgr;
38332     var pos = config.region;
38333     config.skipConfig = true;
38334     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38335     
38336     if (mgr.el) {
38337         this.onRender(mgr.el);   
38338     }
38339      
38340     this.visible = true;
38341     this.collapsed = false;
38342     this.unrendered_panels = [];
38343 };
38344
38345 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38346
38347     position: '', // set by wrapper (eg. north/south etc..)
38348     unrendered_panels : null,  // unrendered panels.
38349     
38350     tabPosition : false,
38351     
38352     mgr: false, // points to 'Border'
38353     
38354     
38355     createBody : function(){
38356         /** This region's body element 
38357         * @type Roo.Element */
38358         this.bodyEl = this.el.createChild({
38359                 tag: "div",
38360                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38361         });
38362     },
38363
38364     onRender: function(ctr, pos)
38365     {
38366         var dh = Roo.DomHelper;
38367         /** This region's container element 
38368         * @type Roo.Element */
38369         this.el = dh.append(ctr.dom, {
38370                 tag: "div",
38371                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38372             }, true);
38373         /** This region's title element 
38374         * @type Roo.Element */
38375     
38376         this.titleEl = dh.append(this.el.dom,  {
38377                 tag: "div",
38378                 unselectable: "on",
38379                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38380                 children:[
38381                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38382                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38383                 ]
38384             }, true);
38385         
38386         this.titleEl.enableDisplayMode();
38387         /** This region's title text element 
38388         * @type HTMLElement */
38389         this.titleTextEl = this.titleEl.dom.firstChild;
38390         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38391         /*
38392         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38393         this.closeBtn.enableDisplayMode();
38394         this.closeBtn.on("click", this.closeClicked, this);
38395         this.closeBtn.hide();
38396     */
38397         this.createBody(this.config);
38398         if(this.config.hideWhenEmpty){
38399             this.hide();
38400             this.on("paneladded", this.validateVisibility, this);
38401             this.on("panelremoved", this.validateVisibility, this);
38402         }
38403         if(this.autoScroll){
38404             this.bodyEl.setStyle("overflow", "auto");
38405         }else{
38406             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38407         }
38408         //if(c.titlebar !== false){
38409             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38410                 this.titleEl.hide();
38411             }else{
38412                 this.titleEl.show();
38413                 if(this.config.title){
38414                     this.titleTextEl.innerHTML = this.config.title;
38415                 }
38416             }
38417         //}
38418         if(this.config.collapsed){
38419             this.collapse(true);
38420         }
38421         if(this.config.hidden){
38422             this.hide();
38423         }
38424         
38425         if (this.unrendered_panels && this.unrendered_panels.length) {
38426             for (var i =0;i< this.unrendered_panels.length; i++) {
38427                 this.add(this.unrendered_panels[i]);
38428             }
38429             this.unrendered_panels = null;
38430             
38431         }
38432         
38433     },
38434     
38435     applyConfig : function(c)
38436     {
38437         /*
38438          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38439             var dh = Roo.DomHelper;
38440             if(c.titlebar !== false){
38441                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38442                 this.collapseBtn.on("click", this.collapse, this);
38443                 this.collapseBtn.enableDisplayMode();
38444                 /*
38445                 if(c.showPin === true || this.showPin){
38446                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38447                     this.stickBtn.enableDisplayMode();
38448                     this.stickBtn.on("click", this.expand, this);
38449                     this.stickBtn.hide();
38450                 }
38451                 
38452             }
38453             */
38454             /** This region's collapsed element
38455             * @type Roo.Element */
38456             /*
38457              *
38458             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38459                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38460             ]}, true);
38461             
38462             if(c.floatable !== false){
38463                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38464                this.collapsedEl.on("click", this.collapseClick, this);
38465             }
38466
38467             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38468                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38469                    id: "message", unselectable: "on", style:{"float":"left"}});
38470                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38471              }
38472             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38473             this.expandBtn.on("click", this.expand, this);
38474             
38475         }
38476         
38477         if(this.collapseBtn){
38478             this.collapseBtn.setVisible(c.collapsible == true);
38479         }
38480         
38481         this.cmargins = c.cmargins || this.cmargins ||
38482                          (this.position == "west" || this.position == "east" ?
38483                              {top: 0, left: 2, right:2, bottom: 0} :
38484                              {top: 2, left: 0, right:0, bottom: 2});
38485         */
38486         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38487         
38488         
38489         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38490         
38491         this.autoScroll = c.autoScroll || false;
38492         
38493         
38494        
38495         
38496         this.duration = c.duration || .30;
38497         this.slideDuration = c.slideDuration || .45;
38498         this.config = c;
38499        
38500     },
38501     /**
38502      * Returns true if this region is currently visible.
38503      * @return {Boolean}
38504      */
38505     isVisible : function(){
38506         return this.visible;
38507     },
38508
38509     /**
38510      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38511      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38512      */
38513     //setCollapsedTitle : function(title){
38514     //    title = title || "&#160;";
38515      //   if(this.collapsedTitleTextEl){
38516       //      this.collapsedTitleTextEl.innerHTML = title;
38517        // }
38518     //},
38519
38520     getBox : function(){
38521         var b;
38522       //  if(!this.collapsed){
38523             b = this.el.getBox(false, true);
38524        // }else{
38525           //  b = this.collapsedEl.getBox(false, true);
38526         //}
38527         return b;
38528     },
38529
38530     getMargins : function(){
38531         return this.margins;
38532         //return this.collapsed ? this.cmargins : this.margins;
38533     },
38534 /*
38535     highlight : function(){
38536         this.el.addClass("x-layout-panel-dragover");
38537     },
38538
38539     unhighlight : function(){
38540         this.el.removeClass("x-layout-panel-dragover");
38541     },
38542 */
38543     updateBox : function(box)
38544     {
38545         if (!this.bodyEl) {
38546             return; // not rendered yet..
38547         }
38548         
38549         this.box = box;
38550         if(!this.collapsed){
38551             this.el.dom.style.left = box.x + "px";
38552             this.el.dom.style.top = box.y + "px";
38553             this.updateBody(box.width, box.height);
38554         }else{
38555             this.collapsedEl.dom.style.left = box.x + "px";
38556             this.collapsedEl.dom.style.top = box.y + "px";
38557             this.collapsedEl.setSize(box.width, box.height);
38558         }
38559         if(this.tabs){
38560             this.tabs.autoSizeTabs();
38561         }
38562     },
38563
38564     updateBody : function(w, h)
38565     {
38566         if(w !== null){
38567             this.el.setWidth(w);
38568             w -= this.el.getBorderWidth("rl");
38569             if(this.config.adjustments){
38570                 w += this.config.adjustments[0];
38571             }
38572         }
38573         if(h !== null && h > 0){
38574             this.el.setHeight(h);
38575             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38576             h -= this.el.getBorderWidth("tb");
38577             if(this.config.adjustments){
38578                 h += this.config.adjustments[1];
38579             }
38580             this.bodyEl.setHeight(h);
38581             if(this.tabs){
38582                 h = this.tabs.syncHeight(h);
38583             }
38584         }
38585         if(this.panelSize){
38586             w = w !== null ? w : this.panelSize.width;
38587             h = h !== null ? h : this.panelSize.height;
38588         }
38589         if(this.activePanel){
38590             var el = this.activePanel.getEl();
38591             w = w !== null ? w : el.getWidth();
38592             h = h !== null ? h : el.getHeight();
38593             this.panelSize = {width: w, height: h};
38594             this.activePanel.setSize(w, h);
38595         }
38596         if(Roo.isIE && this.tabs){
38597             this.tabs.el.repaint();
38598         }
38599     },
38600
38601     /**
38602      * Returns the container element for this region.
38603      * @return {Roo.Element}
38604      */
38605     getEl : function(){
38606         return this.el;
38607     },
38608
38609     /**
38610      * Hides this region.
38611      */
38612     hide : function(){
38613         //if(!this.collapsed){
38614             this.el.dom.style.left = "-2000px";
38615             this.el.hide();
38616         //}else{
38617          //   this.collapsedEl.dom.style.left = "-2000px";
38618          //   this.collapsedEl.hide();
38619        // }
38620         this.visible = false;
38621         this.fireEvent("visibilitychange", this, false);
38622     },
38623
38624     /**
38625      * Shows this region if it was previously hidden.
38626      */
38627     show : function(){
38628         //if(!this.collapsed){
38629             this.el.show();
38630         //}else{
38631         //    this.collapsedEl.show();
38632        // }
38633         this.visible = true;
38634         this.fireEvent("visibilitychange", this, true);
38635     },
38636 /*
38637     closeClicked : function(){
38638         if(this.activePanel){
38639             this.remove(this.activePanel);
38640         }
38641     },
38642
38643     collapseClick : function(e){
38644         if(this.isSlid){
38645            e.stopPropagation();
38646            this.slideIn();
38647         }else{
38648            e.stopPropagation();
38649            this.slideOut();
38650         }
38651     },
38652 */
38653     /**
38654      * Collapses this region.
38655      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38656      */
38657     /*
38658     collapse : function(skipAnim, skipCheck = false){
38659         if(this.collapsed) {
38660             return;
38661         }
38662         
38663         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38664             
38665             this.collapsed = true;
38666             if(this.split){
38667                 this.split.el.hide();
38668             }
38669             if(this.config.animate && skipAnim !== true){
38670                 this.fireEvent("invalidated", this);
38671                 this.animateCollapse();
38672             }else{
38673                 this.el.setLocation(-20000,-20000);
38674                 this.el.hide();
38675                 this.collapsedEl.show();
38676                 this.fireEvent("collapsed", this);
38677                 this.fireEvent("invalidated", this);
38678             }
38679         }
38680         
38681     },
38682 */
38683     animateCollapse : function(){
38684         // overridden
38685     },
38686
38687     /**
38688      * Expands this region if it was previously collapsed.
38689      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38690      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38691      */
38692     /*
38693     expand : function(e, skipAnim){
38694         if(e) {
38695             e.stopPropagation();
38696         }
38697         if(!this.collapsed || this.el.hasActiveFx()) {
38698             return;
38699         }
38700         if(this.isSlid){
38701             this.afterSlideIn();
38702             skipAnim = true;
38703         }
38704         this.collapsed = false;
38705         if(this.config.animate && skipAnim !== true){
38706             this.animateExpand();
38707         }else{
38708             this.el.show();
38709             if(this.split){
38710                 this.split.el.show();
38711             }
38712             this.collapsedEl.setLocation(-2000,-2000);
38713             this.collapsedEl.hide();
38714             this.fireEvent("invalidated", this);
38715             this.fireEvent("expanded", this);
38716         }
38717     },
38718 */
38719     animateExpand : function(){
38720         // overridden
38721     },
38722
38723     initTabs : function()
38724     {
38725         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38726         
38727         var ts = new Roo.bootstrap.panel.Tabs({
38728             el: this.bodyEl.dom,
38729             region : this,
38730             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38731             disableTooltips: this.config.disableTabTips,
38732             toolbar : this.config.toolbar
38733         });
38734         
38735         if(this.config.hideTabs){
38736             ts.stripWrap.setDisplayed(false);
38737         }
38738         this.tabs = ts;
38739         ts.resizeTabs = this.config.resizeTabs === true;
38740         ts.minTabWidth = this.config.minTabWidth || 40;
38741         ts.maxTabWidth = this.config.maxTabWidth || 250;
38742         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38743         ts.monitorResize = false;
38744         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38745         ts.bodyEl.addClass('roo-layout-tabs-body');
38746         this.panels.each(this.initPanelAsTab, this);
38747     },
38748
38749     initPanelAsTab : function(panel){
38750         var ti = this.tabs.addTab(
38751             panel.getEl().id,
38752             panel.getTitle(),
38753             null,
38754             this.config.closeOnTab && panel.isClosable(),
38755             panel.tpl
38756         );
38757         if(panel.tabTip !== undefined){
38758             ti.setTooltip(panel.tabTip);
38759         }
38760         ti.on("activate", function(){
38761               this.setActivePanel(panel);
38762         }, this);
38763         
38764         if(this.config.closeOnTab){
38765             ti.on("beforeclose", function(t, e){
38766                 e.cancel = true;
38767                 this.remove(panel);
38768             }, this);
38769         }
38770         
38771         panel.tabItem = ti;
38772         
38773         return ti;
38774     },
38775
38776     updatePanelTitle : function(panel, title)
38777     {
38778         if(this.activePanel == panel){
38779             this.updateTitle(title);
38780         }
38781         if(this.tabs){
38782             var ti = this.tabs.getTab(panel.getEl().id);
38783             ti.setText(title);
38784             if(panel.tabTip !== undefined){
38785                 ti.setTooltip(panel.tabTip);
38786             }
38787         }
38788     },
38789
38790     updateTitle : function(title){
38791         if(this.titleTextEl && !this.config.title){
38792             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38793         }
38794     },
38795
38796     setActivePanel : function(panel)
38797     {
38798         panel = this.getPanel(panel);
38799         if(this.activePanel && this.activePanel != panel){
38800             if(this.activePanel.setActiveState(false) === false){
38801                 return;
38802             }
38803         }
38804         this.activePanel = panel;
38805         panel.setActiveState(true);
38806         if(this.panelSize){
38807             panel.setSize(this.panelSize.width, this.panelSize.height);
38808         }
38809         if(this.closeBtn){
38810             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38811         }
38812         this.updateTitle(panel.getTitle());
38813         if(this.tabs){
38814             this.fireEvent("invalidated", this);
38815         }
38816         this.fireEvent("panelactivated", this, panel);
38817     },
38818
38819     /**
38820      * Shows the specified panel.
38821      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38822      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38823      */
38824     showPanel : function(panel)
38825     {
38826         panel = this.getPanel(panel);
38827         if(panel){
38828             if(this.tabs){
38829                 var tab = this.tabs.getTab(panel.getEl().id);
38830                 if(tab.isHidden()){
38831                     this.tabs.unhideTab(tab.id);
38832                 }
38833                 tab.activate();
38834             }else{
38835                 this.setActivePanel(panel);
38836             }
38837         }
38838         return panel;
38839     },
38840
38841     /**
38842      * Get the active panel for this region.
38843      * @return {Roo.ContentPanel} The active panel or null
38844      */
38845     getActivePanel : function(){
38846         return this.activePanel;
38847     },
38848
38849     validateVisibility : function(){
38850         if(this.panels.getCount() < 1){
38851             this.updateTitle("&#160;");
38852             this.closeBtn.hide();
38853             this.hide();
38854         }else{
38855             if(!this.isVisible()){
38856                 this.show();
38857             }
38858         }
38859     },
38860
38861     /**
38862      * Adds the passed ContentPanel(s) to this region.
38863      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38864      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38865      */
38866     add : function(panel)
38867     {
38868         if(arguments.length > 1){
38869             for(var i = 0, len = arguments.length; i < len; i++) {
38870                 this.add(arguments[i]);
38871             }
38872             return null;
38873         }
38874         
38875         // if we have not been rendered yet, then we can not really do much of this..
38876         if (!this.bodyEl) {
38877             this.unrendered_panels.push(panel);
38878             return panel;
38879         }
38880         
38881         
38882         
38883         
38884         if(this.hasPanel(panel)){
38885             this.showPanel(panel);
38886             return panel;
38887         }
38888         panel.setRegion(this);
38889         this.panels.add(panel);
38890        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38891             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38892             // and hide them... ???
38893             this.bodyEl.dom.appendChild(panel.getEl().dom);
38894             if(panel.background !== true){
38895                 this.setActivePanel(panel);
38896             }
38897             this.fireEvent("paneladded", this, panel);
38898             return panel;
38899         }
38900         */
38901         if(!this.tabs){
38902             this.initTabs();
38903         }else{
38904             this.initPanelAsTab(panel);
38905         }
38906         
38907         
38908         if(panel.background !== true){
38909             this.tabs.activate(panel.getEl().id);
38910         }
38911         this.fireEvent("paneladded", this, panel);
38912         return panel;
38913     },
38914
38915     /**
38916      * Hides the tab for the specified panel.
38917      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38918      */
38919     hidePanel : function(panel){
38920         if(this.tabs && (panel = this.getPanel(panel))){
38921             this.tabs.hideTab(panel.getEl().id);
38922         }
38923     },
38924
38925     /**
38926      * Unhides the tab for a previously hidden panel.
38927      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38928      */
38929     unhidePanel : function(panel){
38930         if(this.tabs && (panel = this.getPanel(panel))){
38931             this.tabs.unhideTab(panel.getEl().id);
38932         }
38933     },
38934
38935     clearPanels : function(){
38936         while(this.panels.getCount() > 0){
38937              this.remove(this.panels.first());
38938         }
38939     },
38940
38941     /**
38942      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38943      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38944      * @param {Boolean} preservePanel Overrides the config preservePanel option
38945      * @return {Roo.ContentPanel} The panel that was removed
38946      */
38947     remove : function(panel, preservePanel)
38948     {
38949         panel = this.getPanel(panel);
38950         if(!panel){
38951             return null;
38952         }
38953         var e = {};
38954         this.fireEvent("beforeremove", this, panel, e);
38955         if(e.cancel === true){
38956             return null;
38957         }
38958         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38959         var panelId = panel.getId();
38960         this.panels.removeKey(panelId);
38961         if(preservePanel){
38962             document.body.appendChild(panel.getEl().dom);
38963         }
38964         if(this.tabs){
38965             this.tabs.removeTab(panel.getEl().id);
38966         }else if (!preservePanel){
38967             this.bodyEl.dom.removeChild(panel.getEl().dom);
38968         }
38969         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38970             var p = this.panels.first();
38971             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38972             tempEl.appendChild(p.getEl().dom);
38973             this.bodyEl.update("");
38974             this.bodyEl.dom.appendChild(p.getEl().dom);
38975             tempEl = null;
38976             this.updateTitle(p.getTitle());
38977             this.tabs = null;
38978             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38979             this.setActivePanel(p);
38980         }
38981         panel.setRegion(null);
38982         if(this.activePanel == panel){
38983             this.activePanel = null;
38984         }
38985         if(this.config.autoDestroy !== false && preservePanel !== true){
38986             try{panel.destroy();}catch(e){}
38987         }
38988         this.fireEvent("panelremoved", this, panel);
38989         return panel;
38990     },
38991
38992     /**
38993      * Returns the TabPanel component used by this region
38994      * @return {Roo.TabPanel}
38995      */
38996     getTabs : function(){
38997         return this.tabs;
38998     },
38999
39000     createTool : function(parentEl, className){
39001         var btn = Roo.DomHelper.append(parentEl, {
39002             tag: "div",
39003             cls: "x-layout-tools-button",
39004             children: [ {
39005                 tag: "div",
39006                 cls: "roo-layout-tools-button-inner " + className,
39007                 html: "&#160;"
39008             }]
39009         }, true);
39010         btn.addClassOnOver("roo-layout-tools-button-over");
39011         return btn;
39012     }
39013 });/*
39014  * Based on:
39015  * Ext JS Library 1.1.1
39016  * Copyright(c) 2006-2007, Ext JS, LLC.
39017  *
39018  * Originally Released Under LGPL - original licence link has changed is not relivant.
39019  *
39020  * Fork - LGPL
39021  * <script type="text/javascript">
39022  */
39023  
39024
39025
39026 /**
39027  * @class Roo.SplitLayoutRegion
39028  * @extends Roo.LayoutRegion
39029  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39030  */
39031 Roo.bootstrap.layout.Split = function(config){
39032     this.cursor = config.cursor;
39033     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39034 };
39035
39036 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39037 {
39038     splitTip : "Drag to resize.",
39039     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39040     useSplitTips : false,
39041
39042     applyConfig : function(config){
39043         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39044     },
39045     
39046     onRender : function(ctr,pos) {
39047         
39048         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39049         if(!this.config.split){
39050             return;
39051         }
39052         if(!this.split){
39053             
39054             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39055                             tag: "div",
39056                             id: this.el.id + "-split",
39057                             cls: "roo-layout-split roo-layout-split-"+this.position,
39058                             html: "&#160;"
39059             });
39060             /** The SplitBar for this region 
39061             * @type Roo.SplitBar */
39062             // does not exist yet...
39063             Roo.log([this.position, this.orientation]);
39064             
39065             this.split = new Roo.bootstrap.SplitBar({
39066                 dragElement : splitEl,
39067                 resizingElement: this.el,
39068                 orientation : this.orientation
39069             });
39070             
39071             this.split.on("moved", this.onSplitMove, this);
39072             this.split.useShim = this.config.useShim === true;
39073             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39074             if(this.useSplitTips){
39075                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39076             }
39077             //if(config.collapsible){
39078             //    this.split.el.on("dblclick", this.collapse,  this);
39079             //}
39080         }
39081         if(typeof this.config.minSize != "undefined"){
39082             this.split.minSize = this.config.minSize;
39083         }
39084         if(typeof this.config.maxSize != "undefined"){
39085             this.split.maxSize = this.config.maxSize;
39086         }
39087         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39088             this.hideSplitter();
39089         }
39090         
39091     },
39092
39093     getHMaxSize : function(){
39094          var cmax = this.config.maxSize || 10000;
39095          var center = this.mgr.getRegion("center");
39096          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39097     },
39098
39099     getVMaxSize : function(){
39100          var cmax = this.config.maxSize || 10000;
39101          var center = this.mgr.getRegion("center");
39102          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39103     },
39104
39105     onSplitMove : function(split, newSize){
39106         this.fireEvent("resized", this, newSize);
39107     },
39108     
39109     /** 
39110      * Returns the {@link Roo.SplitBar} for this region.
39111      * @return {Roo.SplitBar}
39112      */
39113     getSplitBar : function(){
39114         return this.split;
39115     },
39116     
39117     hide : function(){
39118         this.hideSplitter();
39119         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39120     },
39121
39122     hideSplitter : function(){
39123         if(this.split){
39124             this.split.el.setLocation(-2000,-2000);
39125             this.split.el.hide();
39126         }
39127     },
39128
39129     show : function(){
39130         if(this.split){
39131             this.split.el.show();
39132         }
39133         Roo.bootstrap.layout.Split.superclass.show.call(this);
39134     },
39135     
39136     beforeSlide: function(){
39137         if(Roo.isGecko){// firefox overflow auto bug workaround
39138             this.bodyEl.clip();
39139             if(this.tabs) {
39140                 this.tabs.bodyEl.clip();
39141             }
39142             if(this.activePanel){
39143                 this.activePanel.getEl().clip();
39144                 
39145                 if(this.activePanel.beforeSlide){
39146                     this.activePanel.beforeSlide();
39147                 }
39148             }
39149         }
39150     },
39151     
39152     afterSlide : function(){
39153         if(Roo.isGecko){// firefox overflow auto bug workaround
39154             this.bodyEl.unclip();
39155             if(this.tabs) {
39156                 this.tabs.bodyEl.unclip();
39157             }
39158             if(this.activePanel){
39159                 this.activePanel.getEl().unclip();
39160                 if(this.activePanel.afterSlide){
39161                     this.activePanel.afterSlide();
39162                 }
39163             }
39164         }
39165     },
39166
39167     initAutoHide : function(){
39168         if(this.autoHide !== false){
39169             if(!this.autoHideHd){
39170                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39171                 this.autoHideHd = {
39172                     "mouseout": function(e){
39173                         if(!e.within(this.el, true)){
39174                             st.delay(500);
39175                         }
39176                     },
39177                     "mouseover" : function(e){
39178                         st.cancel();
39179                     },
39180                     scope : this
39181                 };
39182             }
39183             this.el.on(this.autoHideHd);
39184         }
39185     },
39186
39187     clearAutoHide : function(){
39188         if(this.autoHide !== false){
39189             this.el.un("mouseout", this.autoHideHd.mouseout);
39190             this.el.un("mouseover", this.autoHideHd.mouseover);
39191         }
39192     },
39193
39194     clearMonitor : function(){
39195         Roo.get(document).un("click", this.slideInIf, this);
39196     },
39197
39198     // these names are backwards but not changed for compat
39199     slideOut : function(){
39200         if(this.isSlid || this.el.hasActiveFx()){
39201             return;
39202         }
39203         this.isSlid = true;
39204         if(this.collapseBtn){
39205             this.collapseBtn.hide();
39206         }
39207         this.closeBtnState = this.closeBtn.getStyle('display');
39208         this.closeBtn.hide();
39209         if(this.stickBtn){
39210             this.stickBtn.show();
39211         }
39212         this.el.show();
39213         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39214         this.beforeSlide();
39215         this.el.setStyle("z-index", 10001);
39216         this.el.slideIn(this.getSlideAnchor(), {
39217             callback: function(){
39218                 this.afterSlide();
39219                 this.initAutoHide();
39220                 Roo.get(document).on("click", this.slideInIf, this);
39221                 this.fireEvent("slideshow", this);
39222             },
39223             scope: this,
39224             block: true
39225         });
39226     },
39227
39228     afterSlideIn : function(){
39229         this.clearAutoHide();
39230         this.isSlid = false;
39231         this.clearMonitor();
39232         this.el.setStyle("z-index", "");
39233         if(this.collapseBtn){
39234             this.collapseBtn.show();
39235         }
39236         this.closeBtn.setStyle('display', this.closeBtnState);
39237         if(this.stickBtn){
39238             this.stickBtn.hide();
39239         }
39240         this.fireEvent("slidehide", this);
39241     },
39242
39243     slideIn : function(cb){
39244         if(!this.isSlid || this.el.hasActiveFx()){
39245             Roo.callback(cb);
39246             return;
39247         }
39248         this.isSlid = false;
39249         this.beforeSlide();
39250         this.el.slideOut(this.getSlideAnchor(), {
39251             callback: function(){
39252                 this.el.setLeftTop(-10000, -10000);
39253                 this.afterSlide();
39254                 this.afterSlideIn();
39255                 Roo.callback(cb);
39256             },
39257             scope: this,
39258             block: true
39259         });
39260     },
39261     
39262     slideInIf : function(e){
39263         if(!e.within(this.el)){
39264             this.slideIn();
39265         }
39266     },
39267
39268     animateCollapse : function(){
39269         this.beforeSlide();
39270         this.el.setStyle("z-index", 20000);
39271         var anchor = this.getSlideAnchor();
39272         this.el.slideOut(anchor, {
39273             callback : function(){
39274                 this.el.setStyle("z-index", "");
39275                 this.collapsedEl.slideIn(anchor, {duration:.3});
39276                 this.afterSlide();
39277                 this.el.setLocation(-10000,-10000);
39278                 this.el.hide();
39279                 this.fireEvent("collapsed", this);
39280             },
39281             scope: this,
39282             block: true
39283         });
39284     },
39285
39286     animateExpand : function(){
39287         this.beforeSlide();
39288         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39289         this.el.setStyle("z-index", 20000);
39290         this.collapsedEl.hide({
39291             duration:.1
39292         });
39293         this.el.slideIn(this.getSlideAnchor(), {
39294             callback : function(){
39295                 this.el.setStyle("z-index", "");
39296                 this.afterSlide();
39297                 if(this.split){
39298                     this.split.el.show();
39299                 }
39300                 this.fireEvent("invalidated", this);
39301                 this.fireEvent("expanded", this);
39302             },
39303             scope: this,
39304             block: true
39305         });
39306     },
39307
39308     anchors : {
39309         "west" : "left",
39310         "east" : "right",
39311         "north" : "top",
39312         "south" : "bottom"
39313     },
39314
39315     sanchors : {
39316         "west" : "l",
39317         "east" : "r",
39318         "north" : "t",
39319         "south" : "b"
39320     },
39321
39322     canchors : {
39323         "west" : "tl-tr",
39324         "east" : "tr-tl",
39325         "north" : "tl-bl",
39326         "south" : "bl-tl"
39327     },
39328
39329     getAnchor : function(){
39330         return this.anchors[this.position];
39331     },
39332
39333     getCollapseAnchor : function(){
39334         return this.canchors[this.position];
39335     },
39336
39337     getSlideAnchor : function(){
39338         return this.sanchors[this.position];
39339     },
39340
39341     getAlignAdj : function(){
39342         var cm = this.cmargins;
39343         switch(this.position){
39344             case "west":
39345                 return [0, 0];
39346             break;
39347             case "east":
39348                 return [0, 0];
39349             break;
39350             case "north":
39351                 return [0, 0];
39352             break;
39353             case "south":
39354                 return [0, 0];
39355             break;
39356         }
39357     },
39358
39359     getExpandAdj : function(){
39360         var c = this.collapsedEl, cm = this.cmargins;
39361         switch(this.position){
39362             case "west":
39363                 return [-(cm.right+c.getWidth()+cm.left), 0];
39364             break;
39365             case "east":
39366                 return [cm.right+c.getWidth()+cm.left, 0];
39367             break;
39368             case "north":
39369                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39370             break;
39371             case "south":
39372                 return [0, cm.top+cm.bottom+c.getHeight()];
39373             break;
39374         }
39375     }
39376 });/*
39377  * Based on:
39378  * Ext JS Library 1.1.1
39379  * Copyright(c) 2006-2007, Ext JS, LLC.
39380  *
39381  * Originally Released Under LGPL - original licence link has changed is not relivant.
39382  *
39383  * Fork - LGPL
39384  * <script type="text/javascript">
39385  */
39386 /*
39387  * These classes are private internal classes
39388  */
39389 Roo.bootstrap.layout.Center = function(config){
39390     config.region = "center";
39391     Roo.bootstrap.layout.Region.call(this, config);
39392     this.visible = true;
39393     this.minWidth = config.minWidth || 20;
39394     this.minHeight = config.minHeight || 20;
39395 };
39396
39397 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39398     hide : function(){
39399         // center panel can't be hidden
39400     },
39401     
39402     show : function(){
39403         // center panel can't be hidden
39404     },
39405     
39406     getMinWidth: function(){
39407         return this.minWidth;
39408     },
39409     
39410     getMinHeight: function(){
39411         return this.minHeight;
39412     }
39413 });
39414
39415
39416
39417
39418  
39419
39420
39421
39422
39423
39424
39425 Roo.bootstrap.layout.North = function(config)
39426 {
39427     config.region = 'north';
39428     config.cursor = 'n-resize';
39429     
39430     Roo.bootstrap.layout.Split.call(this, config);
39431     
39432     
39433     if(this.split){
39434         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39435         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39436         this.split.el.addClass("roo-layout-split-v");
39437     }
39438     //var size = config.initialSize || config.height;
39439     //if(this.el && typeof size != "undefined"){
39440     //    this.el.setHeight(size);
39441     //}
39442 };
39443 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39444 {
39445     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39446      
39447      
39448     onRender : function(ctr, pos)
39449     {
39450         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39451         var size = this.config.initialSize || this.config.height;
39452         if(this.el && typeof size != "undefined"){
39453             this.el.setHeight(size);
39454         }
39455     
39456     },
39457     
39458     getBox : function(){
39459         if(this.collapsed){
39460             return this.collapsedEl.getBox();
39461         }
39462         var box = this.el.getBox();
39463         if(this.split){
39464             box.height += this.split.el.getHeight();
39465         }
39466         return box;
39467     },
39468     
39469     updateBox : function(box){
39470         if(this.split && !this.collapsed){
39471             box.height -= this.split.el.getHeight();
39472             this.split.el.setLeft(box.x);
39473             this.split.el.setTop(box.y+box.height);
39474             this.split.el.setWidth(box.width);
39475         }
39476         if(this.collapsed){
39477             this.updateBody(box.width, null);
39478         }
39479         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39480     }
39481 });
39482
39483
39484
39485
39486
39487 Roo.bootstrap.layout.South = function(config){
39488     config.region = 'south';
39489     config.cursor = 's-resize';
39490     Roo.bootstrap.layout.Split.call(this, config);
39491     if(this.split){
39492         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39493         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39494         this.split.el.addClass("roo-layout-split-v");
39495     }
39496     
39497 };
39498
39499 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39500     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39501     
39502     onRender : function(ctr, pos)
39503     {
39504         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39505         var size = this.config.initialSize || this.config.height;
39506         if(this.el && typeof size != "undefined"){
39507             this.el.setHeight(size);
39508         }
39509     
39510     },
39511     
39512     getBox : function(){
39513         if(this.collapsed){
39514             return this.collapsedEl.getBox();
39515         }
39516         var box = this.el.getBox();
39517         if(this.split){
39518             var sh = this.split.el.getHeight();
39519             box.height += sh;
39520             box.y -= sh;
39521         }
39522         return box;
39523     },
39524     
39525     updateBox : function(box){
39526         if(this.split && !this.collapsed){
39527             var sh = this.split.el.getHeight();
39528             box.height -= sh;
39529             box.y += sh;
39530             this.split.el.setLeft(box.x);
39531             this.split.el.setTop(box.y-sh);
39532             this.split.el.setWidth(box.width);
39533         }
39534         if(this.collapsed){
39535             this.updateBody(box.width, null);
39536         }
39537         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39538     }
39539 });
39540
39541 Roo.bootstrap.layout.East = function(config){
39542     config.region = "east";
39543     config.cursor = "e-resize";
39544     Roo.bootstrap.layout.Split.call(this, config);
39545     if(this.split){
39546         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39547         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39548         this.split.el.addClass("roo-layout-split-h");
39549     }
39550     
39551 };
39552 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39553     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39554     
39555     onRender : function(ctr, pos)
39556     {
39557         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39558         var size = this.config.initialSize || this.config.width;
39559         if(this.el && typeof size != "undefined"){
39560             this.el.setWidth(size);
39561         }
39562     
39563     },
39564     
39565     getBox : function(){
39566         if(this.collapsed){
39567             return this.collapsedEl.getBox();
39568         }
39569         var box = this.el.getBox();
39570         if(this.split){
39571             var sw = this.split.el.getWidth();
39572             box.width += sw;
39573             box.x -= sw;
39574         }
39575         return box;
39576     },
39577
39578     updateBox : function(box){
39579         if(this.split && !this.collapsed){
39580             var sw = this.split.el.getWidth();
39581             box.width -= sw;
39582             this.split.el.setLeft(box.x);
39583             this.split.el.setTop(box.y);
39584             this.split.el.setHeight(box.height);
39585             box.x += sw;
39586         }
39587         if(this.collapsed){
39588             this.updateBody(null, box.height);
39589         }
39590         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39591     }
39592 });
39593
39594 Roo.bootstrap.layout.West = function(config){
39595     config.region = "west";
39596     config.cursor = "w-resize";
39597     
39598     Roo.bootstrap.layout.Split.call(this, config);
39599     if(this.split){
39600         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39601         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39602         this.split.el.addClass("roo-layout-split-h");
39603     }
39604     
39605 };
39606 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39607     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39608     
39609     onRender: function(ctr, pos)
39610     {
39611         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39612         var size = this.config.initialSize || this.config.width;
39613         if(typeof size != "undefined"){
39614             this.el.setWidth(size);
39615         }
39616     },
39617     
39618     getBox : function(){
39619         if(this.collapsed){
39620             return this.collapsedEl.getBox();
39621         }
39622         var box = this.el.getBox();
39623         if (box.width == 0) {
39624             box.width = this.config.width; // kludge?
39625         }
39626         if(this.split){
39627             box.width += this.split.el.getWidth();
39628         }
39629         return box;
39630     },
39631     
39632     updateBox : function(box){
39633         if(this.split && !this.collapsed){
39634             var sw = this.split.el.getWidth();
39635             box.width -= sw;
39636             this.split.el.setLeft(box.x+box.width);
39637             this.split.el.setTop(box.y);
39638             this.split.el.setHeight(box.height);
39639         }
39640         if(this.collapsed){
39641             this.updateBody(null, box.height);
39642         }
39643         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39644     }
39645 });Roo.namespace("Roo.bootstrap.panel");/*
39646  * Based on:
39647  * Ext JS Library 1.1.1
39648  * Copyright(c) 2006-2007, Ext JS, LLC.
39649  *
39650  * Originally Released Under LGPL - original licence link has changed is not relivant.
39651  *
39652  * Fork - LGPL
39653  * <script type="text/javascript">
39654  */
39655 /**
39656  * @class Roo.ContentPanel
39657  * @extends Roo.util.Observable
39658  * A basic ContentPanel element.
39659  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39660  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39661  * @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
39662  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39663  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39664  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39665  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39666  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39667  * @cfg {String} title          The title for this panel
39668  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39669  * @cfg {String} url            Calls {@link #setUrl} with this value
39670  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39671  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39672  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39673  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39674  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39675  * @cfg {Boolean} badges render the badges
39676  * @cfg {String} cls  extra classes to use  
39677  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39678
39679  * @constructor
39680  * Create a new ContentPanel.
39681  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39682  * @param {String/Object} config A string to set only the title or a config object
39683  * @param {String} content (optional) Set the HTML content for this panel
39684  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39685  */
39686 Roo.bootstrap.panel.Content = function( config){
39687     
39688     this.tpl = config.tpl || false;
39689     
39690     var el = config.el;
39691     var content = config.content;
39692
39693     if(config.autoCreate){ // xtype is available if this is called from factory
39694         el = Roo.id();
39695     }
39696     this.el = Roo.get(el);
39697     if(!this.el && config && config.autoCreate){
39698         if(typeof config.autoCreate == "object"){
39699             if(!config.autoCreate.id){
39700                 config.autoCreate.id = config.id||el;
39701             }
39702             this.el = Roo.DomHelper.append(document.body,
39703                         config.autoCreate, true);
39704         }else{
39705             var elcfg =  {
39706                 tag: "div",
39707                 cls: (config.cls || '') +
39708                     (config.background ? ' bg-' + config.background : '') +
39709                     " roo-layout-inactive-content",
39710                 id: config.id||el
39711             };
39712             if (config.iframe) {
39713                 elcfg.cn = [
39714                     {
39715                         tag : 'iframe',
39716                         style : 'border: 0px',
39717                         src : 'about:blank'
39718                     }
39719                 ];
39720             }
39721               
39722             if (config.html) {
39723                 elcfg.html = config.html;
39724                 
39725             }
39726                         
39727             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39728             if (config.iframe) {
39729                 this.iframeEl = this.el.select('iframe',true).first();
39730             }
39731             
39732         }
39733     } 
39734     this.closable = false;
39735     this.loaded = false;
39736     this.active = false;
39737    
39738       
39739     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39740         
39741         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39742         
39743         this.wrapEl = this.el; //this.el.wrap();
39744         var ti = [];
39745         if (config.toolbar.items) {
39746             ti = config.toolbar.items ;
39747             delete config.toolbar.items ;
39748         }
39749         
39750         var nitems = [];
39751         this.toolbar.render(this.wrapEl, 'before');
39752         for(var i =0;i < ti.length;i++) {
39753           //  Roo.log(['add child', items[i]]);
39754             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39755         }
39756         this.toolbar.items = nitems;
39757         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39758         delete config.toolbar;
39759         
39760     }
39761     /*
39762     // xtype created footer. - not sure if will work as we normally have to render first..
39763     if (this.footer && !this.footer.el && this.footer.xtype) {
39764         if (!this.wrapEl) {
39765             this.wrapEl = this.el.wrap();
39766         }
39767     
39768         this.footer.container = this.wrapEl.createChild();
39769          
39770         this.footer = Roo.factory(this.footer, Roo);
39771         
39772     }
39773     */
39774     
39775      if(typeof config == "string"){
39776         this.title = config;
39777     }else{
39778         Roo.apply(this, config);
39779     }
39780     
39781     if(this.resizeEl){
39782         this.resizeEl = Roo.get(this.resizeEl, true);
39783     }else{
39784         this.resizeEl = this.el;
39785     }
39786     // handle view.xtype
39787     
39788  
39789     
39790     
39791     this.addEvents({
39792         /**
39793          * @event activate
39794          * Fires when this panel is activated. 
39795          * @param {Roo.ContentPanel} this
39796          */
39797         "activate" : true,
39798         /**
39799          * @event deactivate
39800          * Fires when this panel is activated. 
39801          * @param {Roo.ContentPanel} this
39802          */
39803         "deactivate" : true,
39804
39805         /**
39806          * @event resize
39807          * Fires when this panel is resized if fitToFrame is true.
39808          * @param {Roo.ContentPanel} this
39809          * @param {Number} width The width after any component adjustments
39810          * @param {Number} height The height after any component adjustments
39811          */
39812         "resize" : true,
39813         
39814          /**
39815          * @event render
39816          * Fires when this tab is created
39817          * @param {Roo.ContentPanel} this
39818          */
39819         "render" : true
39820         
39821         
39822         
39823     });
39824     
39825
39826     
39827     
39828     if(this.autoScroll && !this.iframe){
39829         this.resizeEl.setStyle("overflow", "auto");
39830     } else {
39831         // fix randome scrolling
39832         //this.el.on('scroll', function() {
39833         //    Roo.log('fix random scolling');
39834         //    this.scrollTo('top',0); 
39835         //});
39836     }
39837     content = content || this.content;
39838     if(content){
39839         this.setContent(content);
39840     }
39841     if(config && config.url){
39842         this.setUrl(this.url, this.params, this.loadOnce);
39843     }
39844     
39845     
39846     
39847     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39848     
39849     if (this.view && typeof(this.view.xtype) != 'undefined') {
39850         this.view.el = this.el.appendChild(document.createElement("div"));
39851         this.view = Roo.factory(this.view); 
39852         this.view.render  &&  this.view.render(false, '');  
39853     }
39854     
39855     
39856     this.fireEvent('render', this);
39857 };
39858
39859 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39860     
39861     cls : '',
39862     background : '',
39863     
39864     tabTip : '',
39865     
39866     iframe : false,
39867     iframeEl : false,
39868     
39869     setRegion : function(region){
39870         this.region = region;
39871         this.setActiveClass(region && !this.background);
39872     },
39873     
39874     
39875     setActiveClass: function(state)
39876     {
39877         if(state){
39878            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39879            this.el.setStyle('position','relative');
39880         }else{
39881            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39882            this.el.setStyle('position', 'absolute');
39883         } 
39884     },
39885     
39886     /**
39887      * Returns the toolbar for this Panel if one was configured. 
39888      * @return {Roo.Toolbar} 
39889      */
39890     getToolbar : function(){
39891         return this.toolbar;
39892     },
39893     
39894     setActiveState : function(active)
39895     {
39896         this.active = active;
39897         this.setActiveClass(active);
39898         if(!active){
39899             if(this.fireEvent("deactivate", this) === false){
39900                 return false;
39901             }
39902             return true;
39903         }
39904         this.fireEvent("activate", this);
39905         return true;
39906     },
39907     /**
39908      * Updates this panel's element (not for iframe)
39909      * @param {String} content The new content
39910      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39911     */
39912     setContent : function(content, loadScripts){
39913         if (this.iframe) {
39914             return;
39915         }
39916         
39917         this.el.update(content, loadScripts);
39918     },
39919
39920     ignoreResize : function(w, h){
39921         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39922             return true;
39923         }else{
39924             this.lastSize = {width: w, height: h};
39925             return false;
39926         }
39927     },
39928     /**
39929      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39930      * @return {Roo.UpdateManager} The UpdateManager
39931      */
39932     getUpdateManager : function(){
39933         if (this.iframe) {
39934             return false;
39935         }
39936         return this.el.getUpdateManager();
39937     },
39938      /**
39939      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39940      * Does not work with IFRAME contents
39941      * @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:
39942 <pre><code>
39943 panel.load({
39944     url: "your-url.php",
39945     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39946     callback: yourFunction,
39947     scope: yourObject, //(optional scope)
39948     discardUrl: false,
39949     nocache: false,
39950     text: "Loading...",
39951     timeout: 30,
39952     scripts: false
39953 });
39954 </code></pre>
39955      
39956      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39957      * 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.
39958      * @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}
39959      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39960      * @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.
39961      * @return {Roo.ContentPanel} this
39962      */
39963     load : function(){
39964         
39965         if (this.iframe) {
39966             return this;
39967         }
39968         
39969         var um = this.el.getUpdateManager();
39970         um.update.apply(um, arguments);
39971         return this;
39972     },
39973
39974
39975     /**
39976      * 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.
39977      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39978      * @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)
39979      * @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)
39980      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39981      */
39982     setUrl : function(url, params, loadOnce){
39983         if (this.iframe) {
39984             this.iframeEl.dom.src = url;
39985             return false;
39986         }
39987         
39988         if(this.refreshDelegate){
39989             this.removeListener("activate", this.refreshDelegate);
39990         }
39991         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39992         this.on("activate", this.refreshDelegate);
39993         return this.el.getUpdateManager();
39994     },
39995     
39996     _handleRefresh : function(url, params, loadOnce){
39997         if(!loadOnce || !this.loaded){
39998             var updater = this.el.getUpdateManager();
39999             updater.update(url, params, this._setLoaded.createDelegate(this));
40000         }
40001     },
40002     
40003     _setLoaded : function(){
40004         this.loaded = true;
40005     }, 
40006     
40007     /**
40008      * Returns this panel's id
40009      * @return {String} 
40010      */
40011     getId : function(){
40012         return this.el.id;
40013     },
40014     
40015     /** 
40016      * Returns this panel's element - used by regiosn to add.
40017      * @return {Roo.Element} 
40018      */
40019     getEl : function(){
40020         return this.wrapEl || this.el;
40021     },
40022     
40023    
40024     
40025     adjustForComponents : function(width, height)
40026     {
40027         //Roo.log('adjustForComponents ');
40028         if(this.resizeEl != this.el){
40029             width -= this.el.getFrameWidth('lr');
40030             height -= this.el.getFrameWidth('tb');
40031         }
40032         if(this.toolbar){
40033             var te = this.toolbar.getEl();
40034             te.setWidth(width);
40035             height -= te.getHeight();
40036         }
40037         if(this.footer){
40038             var te = this.footer.getEl();
40039             te.setWidth(width);
40040             height -= te.getHeight();
40041         }
40042         
40043         
40044         if(this.adjustments){
40045             width += this.adjustments[0];
40046             height += this.adjustments[1];
40047         }
40048         return {"width": width, "height": height};
40049     },
40050     
40051     setSize : function(width, height){
40052         if(this.fitToFrame && !this.ignoreResize(width, height)){
40053             if(this.fitContainer && this.resizeEl != this.el){
40054                 this.el.setSize(width, height);
40055             }
40056             var size = this.adjustForComponents(width, height);
40057             if (this.iframe) {
40058                 this.iframeEl.setSize(width,height);
40059             }
40060             
40061             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40062             this.fireEvent('resize', this, size.width, size.height);
40063             
40064             
40065         }
40066     },
40067     
40068     /**
40069      * Returns this panel's title
40070      * @return {String} 
40071      */
40072     getTitle : function(){
40073         
40074         if (typeof(this.title) != 'object') {
40075             return this.title;
40076         }
40077         
40078         var t = '';
40079         for (var k in this.title) {
40080             if (!this.title.hasOwnProperty(k)) {
40081                 continue;
40082             }
40083             
40084             if (k.indexOf('-') >= 0) {
40085                 var s = k.split('-');
40086                 for (var i = 0; i<s.length; i++) {
40087                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40088                 }
40089             } else {
40090                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40091             }
40092         }
40093         return t;
40094     },
40095     
40096     /**
40097      * Set this panel's title
40098      * @param {String} title
40099      */
40100     setTitle : function(title){
40101         this.title = title;
40102         if(this.region){
40103             this.region.updatePanelTitle(this, title);
40104         }
40105     },
40106     
40107     /**
40108      * Returns true is this panel was configured to be closable
40109      * @return {Boolean} 
40110      */
40111     isClosable : function(){
40112         return this.closable;
40113     },
40114     
40115     beforeSlide : function(){
40116         this.el.clip();
40117         this.resizeEl.clip();
40118     },
40119     
40120     afterSlide : function(){
40121         this.el.unclip();
40122         this.resizeEl.unclip();
40123     },
40124     
40125     /**
40126      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40127      *   Will fail silently if the {@link #setUrl} method has not been called.
40128      *   This does not activate the panel, just updates its content.
40129      */
40130     refresh : function(){
40131         if(this.refreshDelegate){
40132            this.loaded = false;
40133            this.refreshDelegate();
40134         }
40135     },
40136     
40137     /**
40138      * Destroys this panel
40139      */
40140     destroy : function(){
40141         this.el.removeAllListeners();
40142         var tempEl = document.createElement("span");
40143         tempEl.appendChild(this.el.dom);
40144         tempEl.innerHTML = "";
40145         this.el.remove();
40146         this.el = null;
40147     },
40148     
40149     /**
40150      * form - if the content panel contains a form - this is a reference to it.
40151      * @type {Roo.form.Form}
40152      */
40153     form : false,
40154     /**
40155      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40156      *    This contains a reference to it.
40157      * @type {Roo.View}
40158      */
40159     view : false,
40160     
40161       /**
40162      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40163      * <pre><code>
40164
40165 layout.addxtype({
40166        xtype : 'Form',
40167        items: [ .... ]
40168    }
40169 );
40170
40171 </code></pre>
40172      * @param {Object} cfg Xtype definition of item to add.
40173      */
40174     
40175     
40176     getChildContainer: function () {
40177         return this.getEl();
40178     }
40179     
40180     
40181     /*
40182         var  ret = new Roo.factory(cfg);
40183         return ret;
40184         
40185         
40186         // add form..
40187         if (cfg.xtype.match(/^Form$/)) {
40188             
40189             var el;
40190             //if (this.footer) {
40191             //    el = this.footer.container.insertSibling(false, 'before');
40192             //} else {
40193                 el = this.el.createChild();
40194             //}
40195
40196             this.form = new  Roo.form.Form(cfg);
40197             
40198             
40199             if ( this.form.allItems.length) {
40200                 this.form.render(el.dom);
40201             }
40202             return this.form;
40203         }
40204         // should only have one of theses..
40205         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40206             // views.. should not be just added - used named prop 'view''
40207             
40208             cfg.el = this.el.appendChild(document.createElement("div"));
40209             // factory?
40210             
40211             var ret = new Roo.factory(cfg);
40212              
40213              ret.render && ret.render(false, ''); // render blank..
40214             this.view = ret;
40215             return ret;
40216         }
40217         return false;
40218     }
40219     \*/
40220 });
40221  
40222 /**
40223  * @class Roo.bootstrap.panel.Grid
40224  * @extends Roo.bootstrap.panel.Content
40225  * @constructor
40226  * Create a new GridPanel.
40227  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40228  * @param {Object} config A the config object
40229   
40230  */
40231
40232
40233
40234 Roo.bootstrap.panel.Grid = function(config)
40235 {
40236     
40237       
40238     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40239         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40240
40241     config.el = this.wrapper;
40242     //this.el = this.wrapper;
40243     
40244       if (config.container) {
40245         // ctor'ed from a Border/panel.grid
40246         
40247         
40248         this.wrapper.setStyle("overflow", "hidden");
40249         this.wrapper.addClass('roo-grid-container');
40250
40251     }
40252     
40253     
40254     if(config.toolbar){
40255         var tool_el = this.wrapper.createChild();    
40256         this.toolbar = Roo.factory(config.toolbar);
40257         var ti = [];
40258         if (config.toolbar.items) {
40259             ti = config.toolbar.items ;
40260             delete config.toolbar.items ;
40261         }
40262         
40263         var nitems = [];
40264         this.toolbar.render(tool_el);
40265         for(var i =0;i < ti.length;i++) {
40266           //  Roo.log(['add child', items[i]]);
40267             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40268         }
40269         this.toolbar.items = nitems;
40270         
40271         delete config.toolbar;
40272     }
40273     
40274     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40275     config.grid.scrollBody = true;;
40276     config.grid.monitorWindowResize = false; // turn off autosizing
40277     config.grid.autoHeight = false;
40278     config.grid.autoWidth = false;
40279     
40280     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40281     
40282     if (config.background) {
40283         // render grid on panel activation (if panel background)
40284         this.on('activate', function(gp) {
40285             if (!gp.grid.rendered) {
40286                 gp.grid.render(this.wrapper);
40287                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40288             }
40289         });
40290             
40291     } else {
40292         this.grid.render(this.wrapper);
40293         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40294
40295     }
40296     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40297     // ??? needed ??? config.el = this.wrapper;
40298     
40299     
40300     
40301   
40302     // xtype created footer. - not sure if will work as we normally have to render first..
40303     if (this.footer && !this.footer.el && this.footer.xtype) {
40304         
40305         var ctr = this.grid.getView().getFooterPanel(true);
40306         this.footer.dataSource = this.grid.dataSource;
40307         this.footer = Roo.factory(this.footer, Roo);
40308         this.footer.render(ctr);
40309         
40310     }
40311     
40312     
40313     
40314     
40315      
40316 };
40317
40318 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40319     getId : function(){
40320         return this.grid.id;
40321     },
40322     
40323     /**
40324      * Returns the grid for this panel
40325      * @return {Roo.bootstrap.Table} 
40326      */
40327     getGrid : function(){
40328         return this.grid;    
40329     },
40330     
40331     setSize : function(width, height){
40332         if(!this.ignoreResize(width, height)){
40333             var grid = this.grid;
40334             var size = this.adjustForComponents(width, height);
40335             // tfoot is not a footer?
40336           
40337             
40338             var gridel = grid.getGridEl();
40339             gridel.setSize(size.width, size.height);
40340             
40341             var tbd = grid.getGridEl().select('tbody', true).first();
40342             var thd = grid.getGridEl().select('thead',true).first();
40343             var tbf= grid.getGridEl().select('tfoot', true).first();
40344
40345             if (tbf) {
40346                 size.height -= tbf.getHeight();
40347             }
40348             if (thd) {
40349                 size.height -= thd.getHeight();
40350             }
40351             
40352             tbd.setSize(size.width, size.height );
40353             // this is for the account management tab -seems to work there.
40354             var thd = grid.getGridEl().select('thead',true).first();
40355             //if (tbd) {
40356             //    tbd.setSize(size.width, size.height - thd.getHeight());
40357             //}
40358              
40359             grid.autoSize();
40360         }
40361     },
40362      
40363     
40364     
40365     beforeSlide : function(){
40366         this.grid.getView().scroller.clip();
40367     },
40368     
40369     afterSlide : function(){
40370         this.grid.getView().scroller.unclip();
40371     },
40372     
40373     destroy : function(){
40374         this.grid.destroy();
40375         delete this.grid;
40376         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40377     }
40378 });
40379
40380 /**
40381  * @class Roo.bootstrap.panel.Nest
40382  * @extends Roo.bootstrap.panel.Content
40383  * @constructor
40384  * Create a new Panel, that can contain a layout.Border.
40385  * 
40386  * 
40387  * @param {Roo.BorderLayout} layout The layout for this panel
40388  * @param {String/Object} config A string to set only the title or a config object
40389  */
40390 Roo.bootstrap.panel.Nest = function(config)
40391 {
40392     // construct with only one argument..
40393     /* FIXME - implement nicer consturctors
40394     if (layout.layout) {
40395         config = layout;
40396         layout = config.layout;
40397         delete config.layout;
40398     }
40399     if (layout.xtype && !layout.getEl) {
40400         // then layout needs constructing..
40401         layout = Roo.factory(layout, Roo);
40402     }
40403     */
40404     
40405     config.el =  config.layout.getEl();
40406     
40407     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40408     
40409     config.layout.monitorWindowResize = false; // turn off autosizing
40410     this.layout = config.layout;
40411     this.layout.getEl().addClass("roo-layout-nested-layout");
40412     this.layout.parent = this;
40413     
40414     
40415     
40416     
40417 };
40418
40419 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40420
40421     setSize : function(width, height){
40422         if(!this.ignoreResize(width, height)){
40423             var size = this.adjustForComponents(width, height);
40424             var el = this.layout.getEl();
40425             if (size.height < 1) {
40426                 el.setWidth(size.width);   
40427             } else {
40428                 el.setSize(size.width, size.height);
40429             }
40430             var touch = el.dom.offsetWidth;
40431             this.layout.layout();
40432             // ie requires a double layout on the first pass
40433             if(Roo.isIE && !this.initialized){
40434                 this.initialized = true;
40435                 this.layout.layout();
40436             }
40437         }
40438     },
40439     
40440     // activate all subpanels if not currently active..
40441     
40442     setActiveState : function(active){
40443         this.active = active;
40444         this.setActiveClass(active);
40445         
40446         if(!active){
40447             this.fireEvent("deactivate", this);
40448             return;
40449         }
40450         
40451         this.fireEvent("activate", this);
40452         // not sure if this should happen before or after..
40453         if (!this.layout) {
40454             return; // should not happen..
40455         }
40456         var reg = false;
40457         for (var r in this.layout.regions) {
40458             reg = this.layout.getRegion(r);
40459             if (reg.getActivePanel()) {
40460                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40461                 reg.setActivePanel(reg.getActivePanel());
40462                 continue;
40463             }
40464             if (!reg.panels.length) {
40465                 continue;
40466             }
40467             reg.showPanel(reg.getPanel(0));
40468         }
40469         
40470         
40471         
40472         
40473     },
40474     
40475     /**
40476      * Returns the nested BorderLayout for this panel
40477      * @return {Roo.BorderLayout} 
40478      */
40479     getLayout : function(){
40480         return this.layout;
40481     },
40482     
40483      /**
40484      * Adds a xtype elements to the layout of the nested panel
40485      * <pre><code>
40486
40487 panel.addxtype({
40488        xtype : 'ContentPanel',
40489        region: 'west',
40490        items: [ .... ]
40491    }
40492 );
40493
40494 panel.addxtype({
40495         xtype : 'NestedLayoutPanel',
40496         region: 'west',
40497         layout: {
40498            center: { },
40499            west: { }   
40500         },
40501         items : [ ... list of content panels or nested layout panels.. ]
40502    }
40503 );
40504 </code></pre>
40505      * @param {Object} cfg Xtype definition of item to add.
40506      */
40507     addxtype : function(cfg) {
40508         return this.layout.addxtype(cfg);
40509     
40510     }
40511 });/*
40512  * Based on:
40513  * Ext JS Library 1.1.1
40514  * Copyright(c) 2006-2007, Ext JS, LLC.
40515  *
40516  * Originally Released Under LGPL - original licence link has changed is not relivant.
40517  *
40518  * Fork - LGPL
40519  * <script type="text/javascript">
40520  */
40521 /**
40522  * @class Roo.TabPanel
40523  * @extends Roo.util.Observable
40524  * A lightweight tab container.
40525  * <br><br>
40526  * Usage:
40527  * <pre><code>
40528 // basic tabs 1, built from existing content
40529 var tabs = new Roo.TabPanel("tabs1");
40530 tabs.addTab("script", "View Script");
40531 tabs.addTab("markup", "View Markup");
40532 tabs.activate("script");
40533
40534 // more advanced tabs, built from javascript
40535 var jtabs = new Roo.TabPanel("jtabs");
40536 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40537
40538 // set up the UpdateManager
40539 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40540 var updater = tab2.getUpdateManager();
40541 updater.setDefaultUrl("ajax1.htm");
40542 tab2.on('activate', updater.refresh, updater, true);
40543
40544 // Use setUrl for Ajax loading
40545 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40546 tab3.setUrl("ajax2.htm", null, true);
40547
40548 // Disabled tab
40549 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40550 tab4.disable();
40551
40552 jtabs.activate("jtabs-1");
40553  * </code></pre>
40554  * @constructor
40555  * Create a new TabPanel.
40556  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40557  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40558  */
40559 Roo.bootstrap.panel.Tabs = function(config){
40560     /**
40561     * The container element for this TabPanel.
40562     * @type Roo.Element
40563     */
40564     this.el = Roo.get(config.el);
40565     delete config.el;
40566     if(config){
40567         if(typeof config == "boolean"){
40568             this.tabPosition = config ? "bottom" : "top";
40569         }else{
40570             Roo.apply(this, config);
40571         }
40572     }
40573     
40574     if(this.tabPosition == "bottom"){
40575         // if tabs are at the bottom = create the body first.
40576         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40577         this.el.addClass("roo-tabs-bottom");
40578     }
40579     // next create the tabs holders
40580     
40581     if (this.tabPosition == "west"){
40582         
40583         var reg = this.region; // fake it..
40584         while (reg) {
40585             if (!reg.mgr.parent) {
40586                 break;
40587             }
40588             reg = reg.mgr.parent.region;
40589         }
40590         Roo.log("got nest?");
40591         Roo.log(reg);
40592         if (reg.mgr.getRegion('west')) {
40593             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40594             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40595             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40596             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40597             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40598         
40599             
40600         }
40601         
40602         
40603     } else {
40604      
40605         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40606         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40607         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40608         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40609     }
40610     
40611     
40612     if(Roo.isIE){
40613         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40614     }
40615     
40616     // finally - if tabs are at the top, then create the body last..
40617     if(this.tabPosition != "bottom"){
40618         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40619          * @type Roo.Element
40620          */
40621         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40622         this.el.addClass("roo-tabs-top");
40623     }
40624     this.items = [];
40625
40626     this.bodyEl.setStyle("position", "relative");
40627
40628     this.active = null;
40629     this.activateDelegate = this.activate.createDelegate(this);
40630
40631     this.addEvents({
40632         /**
40633          * @event tabchange
40634          * Fires when the active tab changes
40635          * @param {Roo.TabPanel} this
40636          * @param {Roo.TabPanelItem} activePanel The new active tab
40637          */
40638         "tabchange": true,
40639         /**
40640          * @event beforetabchange
40641          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40642          * @param {Roo.TabPanel} this
40643          * @param {Object} e Set cancel to true on this object to cancel the tab change
40644          * @param {Roo.TabPanelItem} tab The tab being changed to
40645          */
40646         "beforetabchange" : true
40647     });
40648
40649     Roo.EventManager.onWindowResize(this.onResize, this);
40650     this.cpad = this.el.getPadding("lr");
40651     this.hiddenCount = 0;
40652
40653
40654     // toolbar on the tabbar support...
40655     if (this.toolbar) {
40656         alert("no toolbar support yet");
40657         this.toolbar  = false;
40658         /*
40659         var tcfg = this.toolbar;
40660         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40661         this.toolbar = new Roo.Toolbar(tcfg);
40662         if (Roo.isSafari) {
40663             var tbl = tcfg.container.child('table', true);
40664             tbl.setAttribute('width', '100%');
40665         }
40666         */
40667         
40668     }
40669    
40670
40671
40672     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40673 };
40674
40675 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40676     /*
40677      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40678      */
40679     tabPosition : "top",
40680     /*
40681      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40682      */
40683     currentTabWidth : 0,
40684     /*
40685      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40686      */
40687     minTabWidth : 40,
40688     /*
40689      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40690      */
40691     maxTabWidth : 250,
40692     /*
40693      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40694      */
40695     preferredTabWidth : 175,
40696     /*
40697      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40698      */
40699     resizeTabs : false,
40700     /*
40701      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40702      */
40703     monitorResize : true,
40704     /*
40705      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40706      */
40707     toolbar : false,  // set by caller..
40708     
40709     region : false, /// set by caller
40710     
40711     disableTooltips : true, // not used yet...
40712
40713     /**
40714      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40715      * @param {String} id The id of the div to use <b>or create</b>
40716      * @param {String} text The text for the tab
40717      * @param {String} content (optional) Content to put in the TabPanelItem body
40718      * @param {Boolean} closable (optional) True to create a close icon on the tab
40719      * @return {Roo.TabPanelItem} The created TabPanelItem
40720      */
40721     addTab : function(id, text, content, closable, tpl)
40722     {
40723         var item = new Roo.bootstrap.panel.TabItem({
40724             panel: this,
40725             id : id,
40726             text : text,
40727             closable : closable,
40728             tpl : tpl
40729         });
40730         this.addTabItem(item);
40731         if(content){
40732             item.setContent(content);
40733         }
40734         return item;
40735     },
40736
40737     /**
40738      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40739      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40740      * @return {Roo.TabPanelItem}
40741      */
40742     getTab : function(id){
40743         return this.items[id];
40744     },
40745
40746     /**
40747      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40748      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40749      */
40750     hideTab : function(id){
40751         var t = this.items[id];
40752         if(!t.isHidden()){
40753            t.setHidden(true);
40754            this.hiddenCount++;
40755            this.autoSizeTabs();
40756         }
40757     },
40758
40759     /**
40760      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40761      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40762      */
40763     unhideTab : function(id){
40764         var t = this.items[id];
40765         if(t.isHidden()){
40766            t.setHidden(false);
40767            this.hiddenCount--;
40768            this.autoSizeTabs();
40769         }
40770     },
40771
40772     /**
40773      * Adds an existing {@link Roo.TabPanelItem}.
40774      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40775      */
40776     addTabItem : function(item)
40777     {
40778         this.items[item.id] = item;
40779         this.items.push(item);
40780         this.autoSizeTabs();
40781       //  if(this.resizeTabs){
40782     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40783   //         this.autoSizeTabs();
40784 //        }else{
40785 //            item.autoSize();
40786        // }
40787     },
40788
40789     /**
40790      * Removes a {@link Roo.TabPanelItem}.
40791      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40792      */
40793     removeTab : function(id){
40794         var items = this.items;
40795         var tab = items[id];
40796         if(!tab) { return; }
40797         var index = items.indexOf(tab);
40798         if(this.active == tab && items.length > 1){
40799             var newTab = this.getNextAvailable(index);
40800             if(newTab) {
40801                 newTab.activate();
40802             }
40803         }
40804         this.stripEl.dom.removeChild(tab.pnode.dom);
40805         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40806             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40807         }
40808         items.splice(index, 1);
40809         delete this.items[tab.id];
40810         tab.fireEvent("close", tab);
40811         tab.purgeListeners();
40812         this.autoSizeTabs();
40813     },
40814
40815     getNextAvailable : function(start){
40816         var items = this.items;
40817         var index = start;
40818         // look for a next tab that will slide over to
40819         // replace the one being removed
40820         while(index < items.length){
40821             var item = items[++index];
40822             if(item && !item.isHidden()){
40823                 return item;
40824             }
40825         }
40826         // if one isn't found select the previous tab (on the left)
40827         index = start;
40828         while(index >= 0){
40829             var item = items[--index];
40830             if(item && !item.isHidden()){
40831                 return item;
40832             }
40833         }
40834         return null;
40835     },
40836
40837     /**
40838      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40839      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40840      */
40841     disableTab : function(id){
40842         var tab = this.items[id];
40843         if(tab && this.active != tab){
40844             tab.disable();
40845         }
40846     },
40847
40848     /**
40849      * Enables a {@link Roo.TabPanelItem} that is disabled.
40850      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40851      */
40852     enableTab : function(id){
40853         var tab = this.items[id];
40854         tab.enable();
40855     },
40856
40857     /**
40858      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40859      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40860      * @return {Roo.TabPanelItem} The TabPanelItem.
40861      */
40862     activate : function(id)
40863     {
40864         //Roo.log('activite:'  + id);
40865         
40866         var tab = this.items[id];
40867         if(!tab){
40868             return null;
40869         }
40870         if(tab == this.active || tab.disabled){
40871             return tab;
40872         }
40873         var e = {};
40874         this.fireEvent("beforetabchange", this, e, tab);
40875         if(e.cancel !== true && !tab.disabled){
40876             if(this.active){
40877                 this.active.hide();
40878             }
40879             this.active = this.items[id];
40880             this.active.show();
40881             this.fireEvent("tabchange", this, this.active);
40882         }
40883         return tab;
40884     },
40885
40886     /**
40887      * Gets the active {@link Roo.TabPanelItem}.
40888      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40889      */
40890     getActiveTab : function(){
40891         return this.active;
40892     },
40893
40894     /**
40895      * Updates the tab body element to fit the height of the container element
40896      * for overflow scrolling
40897      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40898      */
40899     syncHeight : function(targetHeight){
40900         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40901         var bm = this.bodyEl.getMargins();
40902         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40903         this.bodyEl.setHeight(newHeight);
40904         return newHeight;
40905     },
40906
40907     onResize : function(){
40908         if(this.monitorResize){
40909             this.autoSizeTabs();
40910         }
40911     },
40912
40913     /**
40914      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40915      */
40916     beginUpdate : function(){
40917         this.updating = true;
40918     },
40919
40920     /**
40921      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40922      */
40923     endUpdate : function(){
40924         this.updating = false;
40925         this.autoSizeTabs();
40926     },
40927
40928     /**
40929      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40930      */
40931     autoSizeTabs : function()
40932     {
40933         var count = this.items.length;
40934         var vcount = count - this.hiddenCount;
40935         
40936         if (vcount < 2) {
40937             this.stripEl.hide();
40938         } else {
40939             this.stripEl.show();
40940         }
40941         
40942         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40943             return;
40944         }
40945         
40946         
40947         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40948         var availWidth = Math.floor(w / vcount);
40949         var b = this.stripBody;
40950         if(b.getWidth() > w){
40951             var tabs = this.items;
40952             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40953             if(availWidth < this.minTabWidth){
40954                 /*if(!this.sleft){    // incomplete scrolling code
40955                     this.createScrollButtons();
40956                 }
40957                 this.showScroll();
40958                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40959             }
40960         }else{
40961             if(this.currentTabWidth < this.preferredTabWidth){
40962                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40963             }
40964         }
40965     },
40966
40967     /**
40968      * Returns the number of tabs in this TabPanel.
40969      * @return {Number}
40970      */
40971      getCount : function(){
40972          return this.items.length;
40973      },
40974
40975     /**
40976      * Resizes all the tabs to the passed width
40977      * @param {Number} The new width
40978      */
40979     setTabWidth : function(width){
40980         this.currentTabWidth = width;
40981         for(var i = 0, len = this.items.length; i < len; i++) {
40982                 if(!this.items[i].isHidden()) {
40983                 this.items[i].setWidth(width);
40984             }
40985         }
40986     },
40987
40988     /**
40989      * Destroys this TabPanel
40990      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40991      */
40992     destroy : function(removeEl){
40993         Roo.EventManager.removeResizeListener(this.onResize, this);
40994         for(var i = 0, len = this.items.length; i < len; i++){
40995             this.items[i].purgeListeners();
40996         }
40997         if(removeEl === true){
40998             this.el.update("");
40999             this.el.remove();
41000         }
41001     },
41002     
41003     createStrip : function(container)
41004     {
41005         var strip = document.createElement("nav");
41006         strip.className = Roo.bootstrap.version == 4 ?
41007             "navbar-light bg-light" : 
41008             "navbar navbar-default"; //"x-tabs-wrap";
41009         container.appendChild(strip);
41010         return strip;
41011     },
41012     
41013     createStripList : function(strip)
41014     {
41015         // div wrapper for retard IE
41016         // returns the "tr" element.
41017         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41018         //'<div class="x-tabs-strip-wrap">'+
41019           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41020           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41021         return strip.firstChild; //.firstChild.firstChild.firstChild;
41022     },
41023     createBody : function(container)
41024     {
41025         var body = document.createElement("div");
41026         Roo.id(body, "tab-body");
41027         //Roo.fly(body).addClass("x-tabs-body");
41028         Roo.fly(body).addClass("tab-content");
41029         container.appendChild(body);
41030         return body;
41031     },
41032     createItemBody :function(bodyEl, id){
41033         var body = Roo.getDom(id);
41034         if(!body){
41035             body = document.createElement("div");
41036             body.id = id;
41037         }
41038         //Roo.fly(body).addClass("x-tabs-item-body");
41039         Roo.fly(body).addClass("tab-pane");
41040          bodyEl.insertBefore(body, bodyEl.firstChild);
41041         return body;
41042     },
41043     /** @private */
41044     createStripElements :  function(stripEl, text, closable, tpl)
41045     {
41046         var td = document.createElement("li"); // was td..
41047         td.className = 'nav-item';
41048         
41049         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41050         
41051         
41052         stripEl.appendChild(td);
41053         /*if(closable){
41054             td.className = "x-tabs-closable";
41055             if(!this.closeTpl){
41056                 this.closeTpl = new Roo.Template(
41057                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41058                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41059                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41060                 );
41061             }
41062             var el = this.closeTpl.overwrite(td, {"text": text});
41063             var close = el.getElementsByTagName("div")[0];
41064             var inner = el.getElementsByTagName("em")[0];
41065             return {"el": el, "close": close, "inner": inner};
41066         } else {
41067         */
41068         // not sure what this is..
41069 //            if(!this.tabTpl){
41070                 //this.tabTpl = new Roo.Template(
41071                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41072                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41073                 //);
41074 //                this.tabTpl = new Roo.Template(
41075 //                   '<a href="#">' +
41076 //                   '<span unselectable="on"' +
41077 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41078 //                            ' >{text}</span></a>'
41079 //                );
41080 //                
41081 //            }
41082
41083
41084             var template = tpl || this.tabTpl || false;
41085             
41086             if(!template){
41087                 template =  new Roo.Template(
41088                         Roo.bootstrap.version == 4 ? 
41089                             (
41090                                 '<a class="nav-link" href="#" unselectable="on"' +
41091                                      (this.disableTooltips ? '' : ' title="{text}"') +
41092                                      ' >{text}</a>'
41093                             ) : (
41094                                 '<a class="nav-link" href="#">' +
41095                                 '<span unselectable="on"' +
41096                                          (this.disableTooltips ? '' : ' title="{text}"') +
41097                                     ' >{text}</span></a>'
41098                             )
41099                 );
41100             }
41101             
41102             switch (typeof(template)) {
41103                 case 'object' :
41104                     break;
41105                 case 'string' :
41106                     template = new Roo.Template(template);
41107                     break;
41108                 default :
41109                     break;
41110             }
41111             
41112             var el = template.overwrite(td, {"text": text});
41113             
41114             var inner = el.getElementsByTagName("span")[0];
41115             
41116             return {"el": el, "inner": inner};
41117             
41118     }
41119         
41120     
41121 });
41122
41123 /**
41124  * @class Roo.TabPanelItem
41125  * @extends Roo.util.Observable
41126  * Represents an individual item (tab plus body) in a TabPanel.
41127  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41128  * @param {String} id The id of this TabPanelItem
41129  * @param {String} text The text for the tab of this TabPanelItem
41130  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41131  */
41132 Roo.bootstrap.panel.TabItem = function(config){
41133     /**
41134      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41135      * @type Roo.TabPanel
41136      */
41137     this.tabPanel = config.panel;
41138     /**
41139      * The id for this TabPanelItem
41140      * @type String
41141      */
41142     this.id = config.id;
41143     /** @private */
41144     this.disabled = false;
41145     /** @private */
41146     this.text = config.text;
41147     /** @private */
41148     this.loaded = false;
41149     this.closable = config.closable;
41150
41151     /**
41152      * The body element for this TabPanelItem.
41153      * @type Roo.Element
41154      */
41155     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41156     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41157     this.bodyEl.setStyle("display", "block");
41158     this.bodyEl.setStyle("zoom", "1");
41159     //this.hideAction();
41160
41161     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41162     /** @private */
41163     this.el = Roo.get(els.el);
41164     this.inner = Roo.get(els.inner, true);
41165      this.textEl = Roo.bootstrap.version == 4 ?
41166         this.el : Roo.get(this.el.dom.firstChild, true);
41167
41168     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41169     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41170
41171     
41172 //    this.el.on("mousedown", this.onTabMouseDown, this);
41173     this.el.on("click", this.onTabClick, this);
41174     /** @private */
41175     if(config.closable){
41176         var c = Roo.get(els.close, true);
41177         c.dom.title = this.closeText;
41178         c.addClassOnOver("close-over");
41179         c.on("click", this.closeClick, this);
41180      }
41181
41182     this.addEvents({
41183          /**
41184          * @event activate
41185          * Fires when this tab becomes the active tab.
41186          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41187          * @param {Roo.TabPanelItem} this
41188          */
41189         "activate": true,
41190         /**
41191          * @event beforeclose
41192          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41193          * @param {Roo.TabPanelItem} this
41194          * @param {Object} e Set cancel to true on this object to cancel the close.
41195          */
41196         "beforeclose": true,
41197         /**
41198          * @event close
41199          * Fires when this tab is closed.
41200          * @param {Roo.TabPanelItem} this
41201          */
41202          "close": true,
41203         /**
41204          * @event deactivate
41205          * Fires when this tab is no longer the active tab.
41206          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41207          * @param {Roo.TabPanelItem} this
41208          */
41209          "deactivate" : true
41210     });
41211     this.hidden = false;
41212
41213     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41214 };
41215
41216 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41217            {
41218     purgeListeners : function(){
41219        Roo.util.Observable.prototype.purgeListeners.call(this);
41220        this.el.removeAllListeners();
41221     },
41222     /**
41223      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41224      */
41225     show : function(){
41226         this.status_node.addClass("active");
41227         this.showAction();
41228         if(Roo.isOpera){
41229             this.tabPanel.stripWrap.repaint();
41230         }
41231         this.fireEvent("activate", this.tabPanel, this);
41232     },
41233
41234     /**
41235      * Returns true if this tab is the active tab.
41236      * @return {Boolean}
41237      */
41238     isActive : function(){
41239         return this.tabPanel.getActiveTab() == this;
41240     },
41241
41242     /**
41243      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41244      */
41245     hide : function(){
41246         this.status_node.removeClass("active");
41247         this.hideAction();
41248         this.fireEvent("deactivate", this.tabPanel, this);
41249     },
41250
41251     hideAction : function(){
41252         this.bodyEl.hide();
41253         this.bodyEl.setStyle("position", "absolute");
41254         this.bodyEl.setLeft("-20000px");
41255         this.bodyEl.setTop("-20000px");
41256     },
41257
41258     showAction : function(){
41259         this.bodyEl.setStyle("position", "relative");
41260         this.bodyEl.setTop("");
41261         this.bodyEl.setLeft("");
41262         this.bodyEl.show();
41263     },
41264
41265     /**
41266      * Set the tooltip for the tab.
41267      * @param {String} tooltip The tab's tooltip
41268      */
41269     setTooltip : function(text){
41270         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41271             this.textEl.dom.qtip = text;
41272             this.textEl.dom.removeAttribute('title');
41273         }else{
41274             this.textEl.dom.title = text;
41275         }
41276     },
41277
41278     onTabClick : function(e){
41279         e.preventDefault();
41280         this.tabPanel.activate(this.id);
41281     },
41282
41283     onTabMouseDown : function(e){
41284         e.preventDefault();
41285         this.tabPanel.activate(this.id);
41286     },
41287 /*
41288     getWidth : function(){
41289         return this.inner.getWidth();
41290     },
41291
41292     setWidth : function(width){
41293         var iwidth = width - this.linode.getPadding("lr");
41294         this.inner.setWidth(iwidth);
41295         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41296         this.linode.setWidth(width);
41297     },
41298 */
41299     /**
41300      * Show or hide the tab
41301      * @param {Boolean} hidden True to hide or false to show.
41302      */
41303     setHidden : function(hidden){
41304         this.hidden = hidden;
41305         this.linode.setStyle("display", hidden ? "none" : "");
41306     },
41307
41308     /**
41309      * Returns true if this tab is "hidden"
41310      * @return {Boolean}
41311      */
41312     isHidden : function(){
41313         return this.hidden;
41314     },
41315
41316     /**
41317      * Returns the text for this tab
41318      * @return {String}
41319      */
41320     getText : function(){
41321         return this.text;
41322     },
41323     /*
41324     autoSize : function(){
41325         //this.el.beginMeasure();
41326         this.textEl.setWidth(1);
41327         /*
41328          *  #2804 [new] Tabs in Roojs
41329          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41330          */
41331         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41332         //this.el.endMeasure();
41333     //},
41334
41335     /**
41336      * Sets the text for the tab (Note: this also sets the tooltip text)
41337      * @param {String} text The tab's text and tooltip
41338      */
41339     setText : function(text){
41340         this.text = text;
41341         this.textEl.update(text);
41342         this.setTooltip(text);
41343         //if(!this.tabPanel.resizeTabs){
41344         //    this.autoSize();
41345         //}
41346     },
41347     /**
41348      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41349      */
41350     activate : function(){
41351         this.tabPanel.activate(this.id);
41352     },
41353
41354     /**
41355      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41356      */
41357     disable : function(){
41358         if(this.tabPanel.active != this){
41359             this.disabled = true;
41360             this.status_node.addClass("disabled");
41361         }
41362     },
41363
41364     /**
41365      * Enables this TabPanelItem if it was previously disabled.
41366      */
41367     enable : function(){
41368         this.disabled = false;
41369         this.status_node.removeClass("disabled");
41370     },
41371
41372     /**
41373      * Sets the content for this TabPanelItem.
41374      * @param {String} content The content
41375      * @param {Boolean} loadScripts true to look for and load scripts
41376      */
41377     setContent : function(content, loadScripts){
41378         this.bodyEl.update(content, loadScripts);
41379     },
41380
41381     /**
41382      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41383      * @return {Roo.UpdateManager} The UpdateManager
41384      */
41385     getUpdateManager : function(){
41386         return this.bodyEl.getUpdateManager();
41387     },
41388
41389     /**
41390      * Set a URL to be used to load the content for this TabPanelItem.
41391      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41392      * @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)
41393      * @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)
41394      * @return {Roo.UpdateManager} The UpdateManager
41395      */
41396     setUrl : function(url, params, loadOnce){
41397         if(this.refreshDelegate){
41398             this.un('activate', this.refreshDelegate);
41399         }
41400         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41401         this.on("activate", this.refreshDelegate);
41402         return this.bodyEl.getUpdateManager();
41403     },
41404
41405     /** @private */
41406     _handleRefresh : function(url, params, loadOnce){
41407         if(!loadOnce || !this.loaded){
41408             var updater = this.bodyEl.getUpdateManager();
41409             updater.update(url, params, this._setLoaded.createDelegate(this));
41410         }
41411     },
41412
41413     /**
41414      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41415      *   Will fail silently if the setUrl method has not been called.
41416      *   This does not activate the panel, just updates its content.
41417      */
41418     refresh : function(){
41419         if(this.refreshDelegate){
41420            this.loaded = false;
41421            this.refreshDelegate();
41422         }
41423     },
41424
41425     /** @private */
41426     _setLoaded : function(){
41427         this.loaded = true;
41428     },
41429
41430     /** @private */
41431     closeClick : function(e){
41432         var o = {};
41433         e.stopEvent();
41434         this.fireEvent("beforeclose", this, o);
41435         if(o.cancel !== true){
41436             this.tabPanel.removeTab(this.id);
41437         }
41438     },
41439     /**
41440      * The text displayed in the tooltip for the close icon.
41441      * @type String
41442      */
41443     closeText : "Close this tab"
41444 });
41445 /**
41446 *    This script refer to:
41447 *    Title: International Telephone Input
41448 *    Author: Jack O'Connor
41449 *    Code version:  v12.1.12
41450 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41451 **/
41452
41453 Roo.bootstrap.PhoneInputData = function() {
41454     var d = [
41455       [
41456         "Afghanistan (‫افغانستان‬‎)",
41457         "af",
41458         "93"
41459       ],
41460       [
41461         "Albania (Shqipëri)",
41462         "al",
41463         "355"
41464       ],
41465       [
41466         "Algeria (‫الجزائر‬‎)",
41467         "dz",
41468         "213"
41469       ],
41470       [
41471         "American Samoa",
41472         "as",
41473         "1684"
41474       ],
41475       [
41476         "Andorra",
41477         "ad",
41478         "376"
41479       ],
41480       [
41481         "Angola",
41482         "ao",
41483         "244"
41484       ],
41485       [
41486         "Anguilla",
41487         "ai",
41488         "1264"
41489       ],
41490       [
41491         "Antigua and Barbuda",
41492         "ag",
41493         "1268"
41494       ],
41495       [
41496         "Argentina",
41497         "ar",
41498         "54"
41499       ],
41500       [
41501         "Armenia (Հայաստան)",
41502         "am",
41503         "374"
41504       ],
41505       [
41506         "Aruba",
41507         "aw",
41508         "297"
41509       ],
41510       [
41511         "Australia",
41512         "au",
41513         "61",
41514         0
41515       ],
41516       [
41517         "Austria (Österreich)",
41518         "at",
41519         "43"
41520       ],
41521       [
41522         "Azerbaijan (Azərbaycan)",
41523         "az",
41524         "994"
41525       ],
41526       [
41527         "Bahamas",
41528         "bs",
41529         "1242"
41530       ],
41531       [
41532         "Bahrain (‫البحرين‬‎)",
41533         "bh",
41534         "973"
41535       ],
41536       [
41537         "Bangladesh (বাংলাদেশ)",
41538         "bd",
41539         "880"
41540       ],
41541       [
41542         "Barbados",
41543         "bb",
41544         "1246"
41545       ],
41546       [
41547         "Belarus (Беларусь)",
41548         "by",
41549         "375"
41550       ],
41551       [
41552         "Belgium (België)",
41553         "be",
41554         "32"
41555       ],
41556       [
41557         "Belize",
41558         "bz",
41559         "501"
41560       ],
41561       [
41562         "Benin (Bénin)",
41563         "bj",
41564         "229"
41565       ],
41566       [
41567         "Bermuda",
41568         "bm",
41569         "1441"
41570       ],
41571       [
41572         "Bhutan (འབྲུག)",
41573         "bt",
41574         "975"
41575       ],
41576       [
41577         "Bolivia",
41578         "bo",
41579         "591"
41580       ],
41581       [
41582         "Bosnia and Herzegovina (Босна и Херцеговина)",
41583         "ba",
41584         "387"
41585       ],
41586       [
41587         "Botswana",
41588         "bw",
41589         "267"
41590       ],
41591       [
41592         "Brazil (Brasil)",
41593         "br",
41594         "55"
41595       ],
41596       [
41597         "British Indian Ocean Territory",
41598         "io",
41599         "246"
41600       ],
41601       [
41602         "British Virgin Islands",
41603         "vg",
41604         "1284"
41605       ],
41606       [
41607         "Brunei",
41608         "bn",
41609         "673"
41610       ],
41611       [
41612         "Bulgaria (България)",
41613         "bg",
41614         "359"
41615       ],
41616       [
41617         "Burkina Faso",
41618         "bf",
41619         "226"
41620       ],
41621       [
41622         "Burundi (Uburundi)",
41623         "bi",
41624         "257"
41625       ],
41626       [
41627         "Cambodia (កម្ពុជា)",
41628         "kh",
41629         "855"
41630       ],
41631       [
41632         "Cameroon (Cameroun)",
41633         "cm",
41634         "237"
41635       ],
41636       [
41637         "Canada",
41638         "ca",
41639         "1",
41640         1,
41641         ["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"]
41642       ],
41643       [
41644         "Cape Verde (Kabu Verdi)",
41645         "cv",
41646         "238"
41647       ],
41648       [
41649         "Caribbean Netherlands",
41650         "bq",
41651         "599",
41652         1
41653       ],
41654       [
41655         "Cayman Islands",
41656         "ky",
41657         "1345"
41658       ],
41659       [
41660         "Central African Republic (République centrafricaine)",
41661         "cf",
41662         "236"
41663       ],
41664       [
41665         "Chad (Tchad)",
41666         "td",
41667         "235"
41668       ],
41669       [
41670         "Chile",
41671         "cl",
41672         "56"
41673       ],
41674       [
41675         "China (中国)",
41676         "cn",
41677         "86"
41678       ],
41679       [
41680         "Christmas Island",
41681         "cx",
41682         "61",
41683         2
41684       ],
41685       [
41686         "Cocos (Keeling) Islands",
41687         "cc",
41688         "61",
41689         1
41690       ],
41691       [
41692         "Colombia",
41693         "co",
41694         "57"
41695       ],
41696       [
41697         "Comoros (‫جزر القمر‬‎)",
41698         "km",
41699         "269"
41700       ],
41701       [
41702         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41703         "cd",
41704         "243"
41705       ],
41706       [
41707         "Congo (Republic) (Congo-Brazzaville)",
41708         "cg",
41709         "242"
41710       ],
41711       [
41712         "Cook Islands",
41713         "ck",
41714         "682"
41715       ],
41716       [
41717         "Costa Rica",
41718         "cr",
41719         "506"
41720       ],
41721       [
41722         "Côte d’Ivoire",
41723         "ci",
41724         "225"
41725       ],
41726       [
41727         "Croatia (Hrvatska)",
41728         "hr",
41729         "385"
41730       ],
41731       [
41732         "Cuba",
41733         "cu",
41734         "53"
41735       ],
41736       [
41737         "Curaçao",
41738         "cw",
41739         "599",
41740         0
41741       ],
41742       [
41743         "Cyprus (Κύπρος)",
41744         "cy",
41745         "357"
41746       ],
41747       [
41748         "Czech Republic (Česká republika)",
41749         "cz",
41750         "420"
41751       ],
41752       [
41753         "Denmark (Danmark)",
41754         "dk",
41755         "45"
41756       ],
41757       [
41758         "Djibouti",
41759         "dj",
41760         "253"
41761       ],
41762       [
41763         "Dominica",
41764         "dm",
41765         "1767"
41766       ],
41767       [
41768         "Dominican Republic (República Dominicana)",
41769         "do",
41770         "1",
41771         2,
41772         ["809", "829", "849"]
41773       ],
41774       [
41775         "Ecuador",
41776         "ec",
41777         "593"
41778       ],
41779       [
41780         "Egypt (‫مصر‬‎)",
41781         "eg",
41782         "20"
41783       ],
41784       [
41785         "El Salvador",
41786         "sv",
41787         "503"
41788       ],
41789       [
41790         "Equatorial Guinea (Guinea Ecuatorial)",
41791         "gq",
41792         "240"
41793       ],
41794       [
41795         "Eritrea",
41796         "er",
41797         "291"
41798       ],
41799       [
41800         "Estonia (Eesti)",
41801         "ee",
41802         "372"
41803       ],
41804       [
41805         "Ethiopia",
41806         "et",
41807         "251"
41808       ],
41809       [
41810         "Falkland Islands (Islas Malvinas)",
41811         "fk",
41812         "500"
41813       ],
41814       [
41815         "Faroe Islands (Føroyar)",
41816         "fo",
41817         "298"
41818       ],
41819       [
41820         "Fiji",
41821         "fj",
41822         "679"
41823       ],
41824       [
41825         "Finland (Suomi)",
41826         "fi",
41827         "358",
41828         0
41829       ],
41830       [
41831         "France",
41832         "fr",
41833         "33"
41834       ],
41835       [
41836         "French Guiana (Guyane française)",
41837         "gf",
41838         "594"
41839       ],
41840       [
41841         "French Polynesia (Polynésie française)",
41842         "pf",
41843         "689"
41844       ],
41845       [
41846         "Gabon",
41847         "ga",
41848         "241"
41849       ],
41850       [
41851         "Gambia",
41852         "gm",
41853         "220"
41854       ],
41855       [
41856         "Georgia (საქართველო)",
41857         "ge",
41858         "995"
41859       ],
41860       [
41861         "Germany (Deutschland)",
41862         "de",
41863         "49"
41864       ],
41865       [
41866         "Ghana (Gaana)",
41867         "gh",
41868         "233"
41869       ],
41870       [
41871         "Gibraltar",
41872         "gi",
41873         "350"
41874       ],
41875       [
41876         "Greece (Ελλάδα)",
41877         "gr",
41878         "30"
41879       ],
41880       [
41881         "Greenland (Kalaallit Nunaat)",
41882         "gl",
41883         "299"
41884       ],
41885       [
41886         "Grenada",
41887         "gd",
41888         "1473"
41889       ],
41890       [
41891         "Guadeloupe",
41892         "gp",
41893         "590",
41894         0
41895       ],
41896       [
41897         "Guam",
41898         "gu",
41899         "1671"
41900       ],
41901       [
41902         "Guatemala",
41903         "gt",
41904         "502"
41905       ],
41906       [
41907         "Guernsey",
41908         "gg",
41909         "44",
41910         1
41911       ],
41912       [
41913         "Guinea (Guinée)",
41914         "gn",
41915         "224"
41916       ],
41917       [
41918         "Guinea-Bissau (Guiné Bissau)",
41919         "gw",
41920         "245"
41921       ],
41922       [
41923         "Guyana",
41924         "gy",
41925         "592"
41926       ],
41927       [
41928         "Haiti",
41929         "ht",
41930         "509"
41931       ],
41932       [
41933         "Honduras",
41934         "hn",
41935         "504"
41936       ],
41937       [
41938         "Hong Kong (香港)",
41939         "hk",
41940         "852"
41941       ],
41942       [
41943         "Hungary (Magyarország)",
41944         "hu",
41945         "36"
41946       ],
41947       [
41948         "Iceland (Ísland)",
41949         "is",
41950         "354"
41951       ],
41952       [
41953         "India (भारत)",
41954         "in",
41955         "91"
41956       ],
41957       [
41958         "Indonesia",
41959         "id",
41960         "62"
41961       ],
41962       [
41963         "Iran (‫ایران‬‎)",
41964         "ir",
41965         "98"
41966       ],
41967       [
41968         "Iraq (‫العراق‬‎)",
41969         "iq",
41970         "964"
41971       ],
41972       [
41973         "Ireland",
41974         "ie",
41975         "353"
41976       ],
41977       [
41978         "Isle of Man",
41979         "im",
41980         "44",
41981         2
41982       ],
41983       [
41984         "Israel (‫ישראל‬‎)",
41985         "il",
41986         "972"
41987       ],
41988       [
41989         "Italy (Italia)",
41990         "it",
41991         "39",
41992         0
41993       ],
41994       [
41995         "Jamaica",
41996         "jm",
41997         "1876"
41998       ],
41999       [
42000         "Japan (日本)",
42001         "jp",
42002         "81"
42003       ],
42004       [
42005         "Jersey",
42006         "je",
42007         "44",
42008         3
42009       ],
42010       [
42011         "Jordan (‫الأردن‬‎)",
42012         "jo",
42013         "962"
42014       ],
42015       [
42016         "Kazakhstan (Казахстан)",
42017         "kz",
42018         "7",
42019         1
42020       ],
42021       [
42022         "Kenya",
42023         "ke",
42024         "254"
42025       ],
42026       [
42027         "Kiribati",
42028         "ki",
42029         "686"
42030       ],
42031       [
42032         "Kosovo",
42033         "xk",
42034         "383"
42035       ],
42036       [
42037         "Kuwait (‫الكويت‬‎)",
42038         "kw",
42039         "965"
42040       ],
42041       [
42042         "Kyrgyzstan (Кыргызстан)",
42043         "kg",
42044         "996"
42045       ],
42046       [
42047         "Laos (ລາວ)",
42048         "la",
42049         "856"
42050       ],
42051       [
42052         "Latvia (Latvija)",
42053         "lv",
42054         "371"
42055       ],
42056       [
42057         "Lebanon (‫لبنان‬‎)",
42058         "lb",
42059         "961"
42060       ],
42061       [
42062         "Lesotho",
42063         "ls",
42064         "266"
42065       ],
42066       [
42067         "Liberia",
42068         "lr",
42069         "231"
42070       ],
42071       [
42072         "Libya (‫ليبيا‬‎)",
42073         "ly",
42074         "218"
42075       ],
42076       [
42077         "Liechtenstein",
42078         "li",
42079         "423"
42080       ],
42081       [
42082         "Lithuania (Lietuva)",
42083         "lt",
42084         "370"
42085       ],
42086       [
42087         "Luxembourg",
42088         "lu",
42089         "352"
42090       ],
42091       [
42092         "Macau (澳門)",
42093         "mo",
42094         "853"
42095       ],
42096       [
42097         "Macedonia (FYROM) (Македонија)",
42098         "mk",
42099         "389"
42100       ],
42101       [
42102         "Madagascar (Madagasikara)",
42103         "mg",
42104         "261"
42105       ],
42106       [
42107         "Malawi",
42108         "mw",
42109         "265"
42110       ],
42111       [
42112         "Malaysia",
42113         "my",
42114         "60"
42115       ],
42116       [
42117         "Maldives",
42118         "mv",
42119         "960"
42120       ],
42121       [
42122         "Mali",
42123         "ml",
42124         "223"
42125       ],
42126       [
42127         "Malta",
42128         "mt",
42129         "356"
42130       ],
42131       [
42132         "Marshall Islands",
42133         "mh",
42134         "692"
42135       ],
42136       [
42137         "Martinique",
42138         "mq",
42139         "596"
42140       ],
42141       [
42142         "Mauritania (‫موريتانيا‬‎)",
42143         "mr",
42144         "222"
42145       ],
42146       [
42147         "Mauritius (Moris)",
42148         "mu",
42149         "230"
42150       ],
42151       [
42152         "Mayotte",
42153         "yt",
42154         "262",
42155         1
42156       ],
42157       [
42158         "Mexico (México)",
42159         "mx",
42160         "52"
42161       ],
42162       [
42163         "Micronesia",
42164         "fm",
42165         "691"
42166       ],
42167       [
42168         "Moldova (Republica Moldova)",
42169         "md",
42170         "373"
42171       ],
42172       [
42173         "Monaco",
42174         "mc",
42175         "377"
42176       ],
42177       [
42178         "Mongolia (Монгол)",
42179         "mn",
42180         "976"
42181       ],
42182       [
42183         "Montenegro (Crna Gora)",
42184         "me",
42185         "382"
42186       ],
42187       [
42188         "Montserrat",
42189         "ms",
42190         "1664"
42191       ],
42192       [
42193         "Morocco (‫المغرب‬‎)",
42194         "ma",
42195         "212",
42196         0
42197       ],
42198       [
42199         "Mozambique (Moçambique)",
42200         "mz",
42201         "258"
42202       ],
42203       [
42204         "Myanmar (Burma) (မြန်မာ)",
42205         "mm",
42206         "95"
42207       ],
42208       [
42209         "Namibia (Namibië)",
42210         "na",
42211         "264"
42212       ],
42213       [
42214         "Nauru",
42215         "nr",
42216         "674"
42217       ],
42218       [
42219         "Nepal (नेपाल)",
42220         "np",
42221         "977"
42222       ],
42223       [
42224         "Netherlands (Nederland)",
42225         "nl",
42226         "31"
42227       ],
42228       [
42229         "New Caledonia (Nouvelle-Calédonie)",
42230         "nc",
42231         "687"
42232       ],
42233       [
42234         "New Zealand",
42235         "nz",
42236         "64"
42237       ],
42238       [
42239         "Nicaragua",
42240         "ni",
42241         "505"
42242       ],
42243       [
42244         "Niger (Nijar)",
42245         "ne",
42246         "227"
42247       ],
42248       [
42249         "Nigeria",
42250         "ng",
42251         "234"
42252       ],
42253       [
42254         "Niue",
42255         "nu",
42256         "683"
42257       ],
42258       [
42259         "Norfolk Island",
42260         "nf",
42261         "672"
42262       ],
42263       [
42264         "North Korea (조선 민주주의 인민 공화국)",
42265         "kp",
42266         "850"
42267       ],
42268       [
42269         "Northern Mariana Islands",
42270         "mp",
42271         "1670"
42272       ],
42273       [
42274         "Norway (Norge)",
42275         "no",
42276         "47",
42277         0
42278       ],
42279       [
42280         "Oman (‫عُمان‬‎)",
42281         "om",
42282         "968"
42283       ],
42284       [
42285         "Pakistan (‫پاکستان‬‎)",
42286         "pk",
42287         "92"
42288       ],
42289       [
42290         "Palau",
42291         "pw",
42292         "680"
42293       ],
42294       [
42295         "Palestine (‫فلسطين‬‎)",
42296         "ps",
42297         "970"
42298       ],
42299       [
42300         "Panama (Panamá)",
42301         "pa",
42302         "507"
42303       ],
42304       [
42305         "Papua New Guinea",
42306         "pg",
42307         "675"
42308       ],
42309       [
42310         "Paraguay",
42311         "py",
42312         "595"
42313       ],
42314       [
42315         "Peru (Perú)",
42316         "pe",
42317         "51"
42318       ],
42319       [
42320         "Philippines",
42321         "ph",
42322         "63"
42323       ],
42324       [
42325         "Poland (Polska)",
42326         "pl",
42327         "48"
42328       ],
42329       [
42330         "Portugal",
42331         "pt",
42332         "351"
42333       ],
42334       [
42335         "Puerto Rico",
42336         "pr",
42337         "1",
42338         3,
42339         ["787", "939"]
42340       ],
42341       [
42342         "Qatar (‫قطر‬‎)",
42343         "qa",
42344         "974"
42345       ],
42346       [
42347         "Réunion (La Réunion)",
42348         "re",
42349         "262",
42350         0
42351       ],
42352       [
42353         "Romania (România)",
42354         "ro",
42355         "40"
42356       ],
42357       [
42358         "Russia (Россия)",
42359         "ru",
42360         "7",
42361         0
42362       ],
42363       [
42364         "Rwanda",
42365         "rw",
42366         "250"
42367       ],
42368       [
42369         "Saint Barthélemy",
42370         "bl",
42371         "590",
42372         1
42373       ],
42374       [
42375         "Saint Helena",
42376         "sh",
42377         "290"
42378       ],
42379       [
42380         "Saint Kitts and Nevis",
42381         "kn",
42382         "1869"
42383       ],
42384       [
42385         "Saint Lucia",
42386         "lc",
42387         "1758"
42388       ],
42389       [
42390         "Saint Martin (Saint-Martin (partie française))",
42391         "mf",
42392         "590",
42393         2
42394       ],
42395       [
42396         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42397         "pm",
42398         "508"
42399       ],
42400       [
42401         "Saint Vincent and the Grenadines",
42402         "vc",
42403         "1784"
42404       ],
42405       [
42406         "Samoa",
42407         "ws",
42408         "685"
42409       ],
42410       [
42411         "San Marino",
42412         "sm",
42413         "378"
42414       ],
42415       [
42416         "São Tomé and Príncipe (São Tomé e Príncipe)",
42417         "st",
42418         "239"
42419       ],
42420       [
42421         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42422         "sa",
42423         "966"
42424       ],
42425       [
42426         "Senegal (Sénégal)",
42427         "sn",
42428         "221"
42429       ],
42430       [
42431         "Serbia (Србија)",
42432         "rs",
42433         "381"
42434       ],
42435       [
42436         "Seychelles",
42437         "sc",
42438         "248"
42439       ],
42440       [
42441         "Sierra Leone",
42442         "sl",
42443         "232"
42444       ],
42445       [
42446         "Singapore",
42447         "sg",
42448         "65"
42449       ],
42450       [
42451         "Sint Maarten",
42452         "sx",
42453         "1721"
42454       ],
42455       [
42456         "Slovakia (Slovensko)",
42457         "sk",
42458         "421"
42459       ],
42460       [
42461         "Slovenia (Slovenija)",
42462         "si",
42463         "386"
42464       ],
42465       [
42466         "Solomon Islands",
42467         "sb",
42468         "677"
42469       ],
42470       [
42471         "Somalia (Soomaaliya)",
42472         "so",
42473         "252"
42474       ],
42475       [
42476         "South Africa",
42477         "za",
42478         "27"
42479       ],
42480       [
42481         "South Korea (대한민국)",
42482         "kr",
42483         "82"
42484       ],
42485       [
42486         "South Sudan (‫جنوب السودان‬‎)",
42487         "ss",
42488         "211"
42489       ],
42490       [
42491         "Spain (España)",
42492         "es",
42493         "34"
42494       ],
42495       [
42496         "Sri Lanka (ශ්‍රී ලංකාව)",
42497         "lk",
42498         "94"
42499       ],
42500       [
42501         "Sudan (‫السودان‬‎)",
42502         "sd",
42503         "249"
42504       ],
42505       [
42506         "Suriname",
42507         "sr",
42508         "597"
42509       ],
42510       [
42511         "Svalbard and Jan Mayen",
42512         "sj",
42513         "47",
42514         1
42515       ],
42516       [
42517         "Swaziland",
42518         "sz",
42519         "268"
42520       ],
42521       [
42522         "Sweden (Sverige)",
42523         "se",
42524         "46"
42525       ],
42526       [
42527         "Switzerland (Schweiz)",
42528         "ch",
42529         "41"
42530       ],
42531       [
42532         "Syria (‫سوريا‬‎)",
42533         "sy",
42534         "963"
42535       ],
42536       [
42537         "Taiwan (台灣)",
42538         "tw",
42539         "886"
42540       ],
42541       [
42542         "Tajikistan",
42543         "tj",
42544         "992"
42545       ],
42546       [
42547         "Tanzania",
42548         "tz",
42549         "255"
42550       ],
42551       [
42552         "Thailand (ไทย)",
42553         "th",
42554         "66"
42555       ],
42556       [
42557         "Timor-Leste",
42558         "tl",
42559         "670"
42560       ],
42561       [
42562         "Togo",
42563         "tg",
42564         "228"
42565       ],
42566       [
42567         "Tokelau",
42568         "tk",
42569         "690"
42570       ],
42571       [
42572         "Tonga",
42573         "to",
42574         "676"
42575       ],
42576       [
42577         "Trinidad and Tobago",
42578         "tt",
42579         "1868"
42580       ],
42581       [
42582         "Tunisia (‫تونس‬‎)",
42583         "tn",
42584         "216"
42585       ],
42586       [
42587         "Turkey (Türkiye)",
42588         "tr",
42589         "90"
42590       ],
42591       [
42592         "Turkmenistan",
42593         "tm",
42594         "993"
42595       ],
42596       [
42597         "Turks and Caicos Islands",
42598         "tc",
42599         "1649"
42600       ],
42601       [
42602         "Tuvalu",
42603         "tv",
42604         "688"
42605       ],
42606       [
42607         "U.S. Virgin Islands",
42608         "vi",
42609         "1340"
42610       ],
42611       [
42612         "Uganda",
42613         "ug",
42614         "256"
42615       ],
42616       [
42617         "Ukraine (Україна)",
42618         "ua",
42619         "380"
42620       ],
42621       [
42622         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42623         "ae",
42624         "971"
42625       ],
42626       [
42627         "United Kingdom",
42628         "gb",
42629         "44",
42630         0
42631       ],
42632       [
42633         "United States",
42634         "us",
42635         "1",
42636         0
42637       ],
42638       [
42639         "Uruguay",
42640         "uy",
42641         "598"
42642       ],
42643       [
42644         "Uzbekistan (Oʻzbekiston)",
42645         "uz",
42646         "998"
42647       ],
42648       [
42649         "Vanuatu",
42650         "vu",
42651         "678"
42652       ],
42653       [
42654         "Vatican City (Città del Vaticano)",
42655         "va",
42656         "39",
42657         1
42658       ],
42659       [
42660         "Venezuela",
42661         "ve",
42662         "58"
42663       ],
42664       [
42665         "Vietnam (Việt Nam)",
42666         "vn",
42667         "84"
42668       ],
42669       [
42670         "Wallis and Futuna (Wallis-et-Futuna)",
42671         "wf",
42672         "681"
42673       ],
42674       [
42675         "Western Sahara (‫الصحراء الغربية‬‎)",
42676         "eh",
42677         "212",
42678         1
42679       ],
42680       [
42681         "Yemen (‫اليمن‬‎)",
42682         "ye",
42683         "967"
42684       ],
42685       [
42686         "Zambia",
42687         "zm",
42688         "260"
42689       ],
42690       [
42691         "Zimbabwe",
42692         "zw",
42693         "263"
42694       ],
42695       [
42696         "Åland Islands",
42697         "ax",
42698         "358",
42699         1
42700       ]
42701   ];
42702   
42703   return d;
42704 }/**
42705 *    This script refer to:
42706 *    Title: International Telephone Input
42707 *    Author: Jack O'Connor
42708 *    Code version:  v12.1.12
42709 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42710 **/
42711
42712 /**
42713  * @class Roo.bootstrap.PhoneInput
42714  * @extends Roo.bootstrap.TriggerField
42715  * An input with International dial-code selection
42716  
42717  * @cfg {String} defaultDialCode default '+852'
42718  * @cfg {Array} preferedCountries default []
42719   
42720  * @constructor
42721  * Create a new PhoneInput.
42722  * @param {Object} config Configuration options
42723  */
42724
42725 Roo.bootstrap.PhoneInput = function(config) {
42726     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42727 };
42728
42729 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42730         
42731         listWidth: undefined,
42732         
42733         selectedClass: 'active',
42734         
42735         invalidClass : "has-warning",
42736         
42737         validClass: 'has-success',
42738         
42739         allowed: '0123456789',
42740         
42741         max_length: 15,
42742         
42743         /**
42744          * @cfg {String} defaultDialCode The default dial code when initializing the input
42745          */
42746         defaultDialCode: '+852',
42747         
42748         /**
42749          * @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
42750          */
42751         preferedCountries: false,
42752         
42753         getAutoCreate : function()
42754         {
42755             var data = Roo.bootstrap.PhoneInputData();
42756             var align = this.labelAlign || this.parentLabelAlign();
42757             var id = Roo.id();
42758             
42759             this.allCountries = [];
42760             this.dialCodeMapping = [];
42761             
42762             for (var i = 0; i < data.length; i++) {
42763               var c = data[i];
42764               this.allCountries[i] = {
42765                 name: c[0],
42766                 iso2: c[1],
42767                 dialCode: c[2],
42768                 priority: c[3] || 0,
42769                 areaCodes: c[4] || null
42770               };
42771               this.dialCodeMapping[c[2]] = {
42772                   name: c[0],
42773                   iso2: c[1],
42774                   priority: c[3] || 0,
42775                   areaCodes: c[4] || null
42776               };
42777             }
42778             
42779             var cfg = {
42780                 cls: 'form-group',
42781                 cn: []
42782             };
42783             
42784             var input =  {
42785                 tag: 'input',
42786                 id : id,
42787                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42788                 maxlength: this.max_length,
42789                 cls : 'form-control tel-input',
42790                 autocomplete: 'new-password'
42791             };
42792             
42793             var hiddenInput = {
42794                 tag: 'input',
42795                 type: 'hidden',
42796                 cls: 'hidden-tel-input'
42797             };
42798             
42799             if (this.name) {
42800                 hiddenInput.name = this.name;
42801             }
42802             
42803             if (this.disabled) {
42804                 input.disabled = true;
42805             }
42806             
42807             var flag_container = {
42808                 tag: 'div',
42809                 cls: 'flag-box',
42810                 cn: [
42811                     {
42812                         tag: 'div',
42813                         cls: 'flag'
42814                     },
42815                     {
42816                         tag: 'div',
42817                         cls: 'caret'
42818                     }
42819                 ]
42820             };
42821             
42822             var box = {
42823                 tag: 'div',
42824                 cls: this.hasFeedback ? 'has-feedback' : '',
42825                 cn: [
42826                     hiddenInput,
42827                     input,
42828                     {
42829                         tag: 'input',
42830                         cls: 'dial-code-holder',
42831                         disabled: true
42832                     }
42833                 ]
42834             };
42835             
42836             var container = {
42837                 cls: 'roo-select2-container input-group',
42838                 cn: [
42839                     flag_container,
42840                     box
42841                 ]
42842             };
42843             
42844             if (this.fieldLabel.length) {
42845                 var indicator = {
42846                     tag: 'i',
42847                     tooltip: 'This field is required'
42848                 };
42849                 
42850                 var label = {
42851                     tag: 'label',
42852                     'for':  id,
42853                     cls: 'control-label',
42854                     cn: []
42855                 };
42856                 
42857                 var label_text = {
42858                     tag: 'span',
42859                     html: this.fieldLabel
42860                 };
42861                 
42862                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42863                 label.cn = [
42864                     indicator,
42865                     label_text
42866                 ];
42867                 
42868                 if(this.indicatorpos == 'right') {
42869                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42870                     label.cn = [
42871                         label_text,
42872                         indicator
42873                     ];
42874                 }
42875                 
42876                 if(align == 'left') {
42877                     container = {
42878                         tag: 'div',
42879                         cn: [
42880                             container
42881                         ]
42882                     };
42883                     
42884                     if(this.labelWidth > 12){
42885                         label.style = "width: " + this.labelWidth + 'px';
42886                     }
42887                     if(this.labelWidth < 13 && this.labelmd == 0){
42888                         this.labelmd = this.labelWidth;
42889                     }
42890                     if(this.labellg > 0){
42891                         label.cls += ' col-lg-' + this.labellg;
42892                         input.cls += ' col-lg-' + (12 - this.labellg);
42893                     }
42894                     if(this.labelmd > 0){
42895                         label.cls += ' col-md-' + this.labelmd;
42896                         container.cls += ' col-md-' + (12 - this.labelmd);
42897                     }
42898                     if(this.labelsm > 0){
42899                         label.cls += ' col-sm-' + this.labelsm;
42900                         container.cls += ' col-sm-' + (12 - this.labelsm);
42901                     }
42902                     if(this.labelxs > 0){
42903                         label.cls += ' col-xs-' + this.labelxs;
42904                         container.cls += ' col-xs-' + (12 - this.labelxs);
42905                     }
42906                 }
42907             }
42908             
42909             cfg.cn = [
42910                 label,
42911                 container
42912             ];
42913             
42914             var settings = this;
42915             
42916             ['xs','sm','md','lg'].map(function(size){
42917                 if (settings[size]) {
42918                     cfg.cls += ' col-' + size + '-' + settings[size];
42919                 }
42920             });
42921             
42922             this.store = new Roo.data.Store({
42923                 proxy : new Roo.data.MemoryProxy({}),
42924                 reader : new Roo.data.JsonReader({
42925                     fields : [
42926                         {
42927                             'name' : 'name',
42928                             'type' : 'string'
42929                         },
42930                         {
42931                             'name' : 'iso2',
42932                             'type' : 'string'
42933                         },
42934                         {
42935                             'name' : 'dialCode',
42936                             'type' : 'string'
42937                         },
42938                         {
42939                             'name' : 'priority',
42940                             'type' : 'string'
42941                         },
42942                         {
42943                             'name' : 'areaCodes',
42944                             'type' : 'string'
42945                         }
42946                     ]
42947                 })
42948             });
42949             
42950             if(!this.preferedCountries) {
42951                 this.preferedCountries = [
42952                     'hk',
42953                     'gb',
42954                     'us'
42955                 ];
42956             }
42957             
42958             var p = this.preferedCountries.reverse();
42959             
42960             if(p) {
42961                 for (var i = 0; i < p.length; i++) {
42962                     for (var j = 0; j < this.allCountries.length; j++) {
42963                         if(this.allCountries[j].iso2 == p[i]) {
42964                             var t = this.allCountries[j];
42965                             this.allCountries.splice(j,1);
42966                             this.allCountries.unshift(t);
42967                         }
42968                     } 
42969                 }
42970             }
42971             
42972             this.store.proxy.data = {
42973                 success: true,
42974                 data: this.allCountries
42975             };
42976             
42977             return cfg;
42978         },
42979         
42980         initEvents : function()
42981         {
42982             this.createList();
42983             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42984             
42985             this.indicator = this.indicatorEl();
42986             this.flag = this.flagEl();
42987             this.dialCodeHolder = this.dialCodeHolderEl();
42988             
42989             this.trigger = this.el.select('div.flag-box',true).first();
42990             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42991             
42992             var _this = this;
42993             
42994             (function(){
42995                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42996                 _this.list.setWidth(lw);
42997             }).defer(100);
42998             
42999             this.list.on('mouseover', this.onViewOver, this);
43000             this.list.on('mousemove', this.onViewMove, this);
43001             this.inputEl().on("keyup", this.onKeyUp, this);
43002             this.inputEl().on("keypress", this.onKeyPress, this);
43003             
43004             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43005
43006             this.view = new Roo.View(this.list, this.tpl, {
43007                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43008             });
43009             
43010             this.view.on('click', this.onViewClick, this);
43011             this.setValue(this.defaultDialCode);
43012         },
43013         
43014         onTriggerClick : function(e)
43015         {
43016             Roo.log('trigger click');
43017             if(this.disabled){
43018                 return;
43019             }
43020             
43021             if(this.isExpanded()){
43022                 this.collapse();
43023                 this.hasFocus = false;
43024             }else {
43025                 this.store.load({});
43026                 this.hasFocus = true;
43027                 this.expand();
43028             }
43029         },
43030         
43031         isExpanded : function()
43032         {
43033             return this.list.isVisible();
43034         },
43035         
43036         collapse : function()
43037         {
43038             if(!this.isExpanded()){
43039                 return;
43040             }
43041             this.list.hide();
43042             Roo.get(document).un('mousedown', this.collapseIf, this);
43043             Roo.get(document).un('mousewheel', this.collapseIf, this);
43044             this.fireEvent('collapse', this);
43045             this.validate();
43046         },
43047         
43048         expand : function()
43049         {
43050             Roo.log('expand');
43051
43052             if(this.isExpanded() || !this.hasFocus){
43053                 return;
43054             }
43055             
43056             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43057             this.list.setWidth(lw);
43058             
43059             this.list.show();
43060             this.restrictHeight();
43061             
43062             Roo.get(document).on('mousedown', this.collapseIf, this);
43063             Roo.get(document).on('mousewheel', this.collapseIf, this);
43064             
43065             this.fireEvent('expand', this);
43066         },
43067         
43068         restrictHeight : function()
43069         {
43070             this.list.alignTo(this.inputEl(), this.listAlign);
43071             this.list.alignTo(this.inputEl(), this.listAlign);
43072         },
43073         
43074         onViewOver : function(e, t)
43075         {
43076             if(this.inKeyMode){
43077                 return;
43078             }
43079             var item = this.view.findItemFromChild(t);
43080             
43081             if(item){
43082                 var index = this.view.indexOf(item);
43083                 this.select(index, false);
43084             }
43085         },
43086
43087         // private
43088         onViewClick : function(view, doFocus, el, e)
43089         {
43090             var index = this.view.getSelectedIndexes()[0];
43091             
43092             var r = this.store.getAt(index);
43093             
43094             if(r){
43095                 this.onSelect(r, index);
43096             }
43097             if(doFocus !== false && !this.blockFocus){
43098                 this.inputEl().focus();
43099             }
43100         },
43101         
43102         onViewMove : function(e, t)
43103         {
43104             this.inKeyMode = false;
43105         },
43106         
43107         select : function(index, scrollIntoView)
43108         {
43109             this.selectedIndex = index;
43110             this.view.select(index);
43111             if(scrollIntoView !== false){
43112                 var el = this.view.getNode(index);
43113                 if(el){
43114                     this.list.scrollChildIntoView(el, false);
43115                 }
43116             }
43117         },
43118         
43119         createList : function()
43120         {
43121             this.list = Roo.get(document.body).createChild({
43122                 tag: 'ul',
43123                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43124                 style: 'display:none'
43125             });
43126             
43127             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43128         },
43129         
43130         collapseIf : function(e)
43131         {
43132             var in_combo  = e.within(this.el);
43133             var in_list =  e.within(this.list);
43134             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43135             
43136             if (in_combo || in_list || is_list) {
43137                 return;
43138             }
43139             this.collapse();
43140         },
43141         
43142         onSelect : function(record, index)
43143         {
43144             if(this.fireEvent('beforeselect', this, record, index) !== false){
43145                 
43146                 this.setFlagClass(record.data.iso2);
43147                 this.setDialCode(record.data.dialCode);
43148                 this.hasFocus = false;
43149                 this.collapse();
43150                 this.fireEvent('select', this, record, index);
43151             }
43152         },
43153         
43154         flagEl : function()
43155         {
43156             var flag = this.el.select('div.flag',true).first();
43157             if(!flag){
43158                 return false;
43159             }
43160             return flag;
43161         },
43162         
43163         dialCodeHolderEl : function()
43164         {
43165             var d = this.el.select('input.dial-code-holder',true).first();
43166             if(!d){
43167                 return false;
43168             }
43169             return d;
43170         },
43171         
43172         setDialCode : function(v)
43173         {
43174             this.dialCodeHolder.dom.value = '+'+v;
43175         },
43176         
43177         setFlagClass : function(n)
43178         {
43179             this.flag.dom.className = 'flag '+n;
43180         },
43181         
43182         getValue : function()
43183         {
43184             var v = this.inputEl().getValue();
43185             if(this.dialCodeHolder) {
43186                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43187             }
43188             return v;
43189         },
43190         
43191         setValue : function(v)
43192         {
43193             var d = this.getDialCode(v);
43194             
43195             //invalid dial code
43196             if(v.length == 0 || !d || d.length == 0) {
43197                 if(this.rendered){
43198                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43199                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43200                 }
43201                 return;
43202             }
43203             
43204             //valid dial code
43205             this.setFlagClass(this.dialCodeMapping[d].iso2);
43206             this.setDialCode(d);
43207             this.inputEl().dom.value = v.replace('+'+d,'');
43208             this.hiddenEl().dom.value = this.getValue();
43209             
43210             this.validate();
43211         },
43212         
43213         getDialCode : function(v)
43214         {
43215             v = v ||  '';
43216             
43217             if (v.length == 0) {
43218                 return this.dialCodeHolder.dom.value;
43219             }
43220             
43221             var dialCode = "";
43222             if (v.charAt(0) != "+") {
43223                 return false;
43224             }
43225             var numericChars = "";
43226             for (var i = 1; i < v.length; i++) {
43227               var c = v.charAt(i);
43228               if (!isNaN(c)) {
43229                 numericChars += c;
43230                 if (this.dialCodeMapping[numericChars]) {
43231                   dialCode = v.substr(1, i);
43232                 }
43233                 if (numericChars.length == 4) {
43234                   break;
43235                 }
43236               }
43237             }
43238             return dialCode;
43239         },
43240         
43241         reset : function()
43242         {
43243             this.setValue(this.defaultDialCode);
43244             this.validate();
43245         },
43246         
43247         hiddenEl : function()
43248         {
43249             return this.el.select('input.hidden-tel-input',true).first();
43250         },
43251         
43252         // after setting val
43253         onKeyUp : function(e){
43254             this.setValue(this.getValue());
43255         },
43256         
43257         onKeyPress : function(e){
43258             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43259                 e.stopEvent();
43260             }
43261         }
43262         
43263 });
43264 /**
43265  * @class Roo.bootstrap.MoneyField
43266  * @extends Roo.bootstrap.ComboBox
43267  * Bootstrap MoneyField class
43268  * 
43269  * @constructor
43270  * Create a new MoneyField.
43271  * @param {Object} config Configuration options
43272  */
43273
43274 Roo.bootstrap.MoneyField = function(config) {
43275     
43276     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43277     
43278 };
43279
43280 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43281     
43282     /**
43283      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43284      */
43285     allowDecimals : true,
43286     /**
43287      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43288      */
43289     decimalSeparator : ".",
43290     /**
43291      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43292      */
43293     decimalPrecision : 0,
43294     /**
43295      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43296      */
43297     allowNegative : true,
43298     /**
43299      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43300      */
43301     allowZero: true,
43302     /**
43303      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43304      */
43305     minValue : Number.NEGATIVE_INFINITY,
43306     /**
43307      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43308      */
43309     maxValue : Number.MAX_VALUE,
43310     /**
43311      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43312      */
43313     minText : "The minimum value for this field is {0}",
43314     /**
43315      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43316      */
43317     maxText : "The maximum value for this field is {0}",
43318     /**
43319      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43320      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43321      */
43322     nanText : "{0} is not a valid number",
43323     /**
43324      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43325      */
43326     castInt : true,
43327     /**
43328      * @cfg {String} defaults currency of the MoneyField
43329      * value should be in lkey
43330      */
43331     defaultCurrency : false,
43332     /**
43333      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43334      */
43335     thousandsDelimiter : false,
43336     /**
43337      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43338      */
43339     max_length: false,
43340     
43341     inputlg : 9,
43342     inputmd : 9,
43343     inputsm : 9,
43344     inputxs : 6,
43345     
43346     store : false,
43347     
43348     getAutoCreate : function()
43349     {
43350         var align = this.labelAlign || this.parentLabelAlign();
43351         
43352         var id = Roo.id();
43353
43354         var cfg = {
43355             cls: 'form-group',
43356             cn: []
43357         };
43358
43359         var input =  {
43360             tag: 'input',
43361             id : id,
43362             cls : 'form-control roo-money-amount-input',
43363             autocomplete: 'new-password'
43364         };
43365         
43366         var hiddenInput = {
43367             tag: 'input',
43368             type: 'hidden',
43369             id: Roo.id(),
43370             cls: 'hidden-number-input'
43371         };
43372         
43373         if(this.max_length) {
43374             input.maxlength = this.max_length; 
43375         }
43376         
43377         if (this.name) {
43378             hiddenInput.name = this.name;
43379         }
43380
43381         if (this.disabled) {
43382             input.disabled = true;
43383         }
43384
43385         var clg = 12 - this.inputlg;
43386         var cmd = 12 - this.inputmd;
43387         var csm = 12 - this.inputsm;
43388         var cxs = 12 - this.inputxs;
43389         
43390         var container = {
43391             tag : 'div',
43392             cls : 'row roo-money-field',
43393             cn : [
43394                 {
43395                     tag : 'div',
43396                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43397                     cn : [
43398                         {
43399                             tag : 'div',
43400                             cls: 'roo-select2-container input-group',
43401                             cn: [
43402                                 {
43403                                     tag : 'input',
43404                                     cls : 'form-control roo-money-currency-input',
43405                                     autocomplete: 'new-password',
43406                                     readOnly : 1,
43407                                     name : this.currencyName
43408                                 },
43409                                 {
43410                                     tag :'span',
43411                                     cls : 'input-group-addon',
43412                                     cn : [
43413                                         {
43414                                             tag: 'span',
43415                                             cls: 'caret'
43416                                         }
43417                                     ]
43418                                 }
43419                             ]
43420                         }
43421                     ]
43422                 },
43423                 {
43424                     tag : 'div',
43425                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43426                     cn : [
43427                         {
43428                             tag: 'div',
43429                             cls: this.hasFeedback ? 'has-feedback' : '',
43430                             cn: [
43431                                 input
43432                             ]
43433                         }
43434                     ]
43435                 }
43436             ]
43437             
43438         };
43439         
43440         if (this.fieldLabel.length) {
43441             var indicator = {
43442                 tag: 'i',
43443                 tooltip: 'This field is required'
43444             };
43445
43446             var label = {
43447                 tag: 'label',
43448                 'for':  id,
43449                 cls: 'control-label',
43450                 cn: []
43451             };
43452
43453             var label_text = {
43454                 tag: 'span',
43455                 html: this.fieldLabel
43456             };
43457
43458             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43459             label.cn = [
43460                 indicator,
43461                 label_text
43462             ];
43463
43464             if(this.indicatorpos == 'right') {
43465                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43466                 label.cn = [
43467                     label_text,
43468                     indicator
43469                 ];
43470             }
43471
43472             if(align == 'left') {
43473                 container = {
43474                     tag: 'div',
43475                     cn: [
43476                         container
43477                     ]
43478                 };
43479
43480                 if(this.labelWidth > 12){
43481                     label.style = "width: " + this.labelWidth + 'px';
43482                 }
43483                 if(this.labelWidth < 13 && this.labelmd == 0){
43484                     this.labelmd = this.labelWidth;
43485                 }
43486                 if(this.labellg > 0){
43487                     label.cls += ' col-lg-' + this.labellg;
43488                     input.cls += ' col-lg-' + (12 - this.labellg);
43489                 }
43490                 if(this.labelmd > 0){
43491                     label.cls += ' col-md-' + this.labelmd;
43492                     container.cls += ' col-md-' + (12 - this.labelmd);
43493                 }
43494                 if(this.labelsm > 0){
43495                     label.cls += ' col-sm-' + this.labelsm;
43496                     container.cls += ' col-sm-' + (12 - this.labelsm);
43497                 }
43498                 if(this.labelxs > 0){
43499                     label.cls += ' col-xs-' + this.labelxs;
43500                     container.cls += ' col-xs-' + (12 - this.labelxs);
43501                 }
43502             }
43503         }
43504
43505         cfg.cn = [
43506             label,
43507             container,
43508             hiddenInput
43509         ];
43510         
43511         var settings = this;
43512
43513         ['xs','sm','md','lg'].map(function(size){
43514             if (settings[size]) {
43515                 cfg.cls += ' col-' + size + '-' + settings[size];
43516             }
43517         });
43518         
43519         return cfg;
43520     },
43521     
43522     initEvents : function()
43523     {
43524         this.indicator = this.indicatorEl();
43525         
43526         this.initCurrencyEvent();
43527         
43528         this.initNumberEvent();
43529     },
43530     
43531     initCurrencyEvent : function()
43532     {
43533         if (!this.store) {
43534             throw "can not find store for combo";
43535         }
43536         
43537         this.store = Roo.factory(this.store, Roo.data);
43538         this.store.parent = this;
43539         
43540         this.createList();
43541         
43542         this.triggerEl = this.el.select('.input-group-addon', true).first();
43543         
43544         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43545         
43546         var _this = this;
43547         
43548         (function(){
43549             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43550             _this.list.setWidth(lw);
43551         }).defer(100);
43552         
43553         this.list.on('mouseover', this.onViewOver, this);
43554         this.list.on('mousemove', this.onViewMove, this);
43555         this.list.on('scroll', this.onViewScroll, this);
43556         
43557         if(!this.tpl){
43558             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43559         }
43560         
43561         this.view = new Roo.View(this.list, this.tpl, {
43562             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43563         });
43564         
43565         this.view.on('click', this.onViewClick, this);
43566         
43567         this.store.on('beforeload', this.onBeforeLoad, this);
43568         this.store.on('load', this.onLoad, this);
43569         this.store.on('loadexception', this.onLoadException, this);
43570         
43571         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43572             "up" : function(e){
43573                 this.inKeyMode = true;
43574                 this.selectPrev();
43575             },
43576
43577             "down" : function(e){
43578                 if(!this.isExpanded()){
43579                     this.onTriggerClick();
43580                 }else{
43581                     this.inKeyMode = true;
43582                     this.selectNext();
43583                 }
43584             },
43585
43586             "enter" : function(e){
43587                 this.collapse();
43588                 
43589                 if(this.fireEvent("specialkey", this, e)){
43590                     this.onViewClick(false);
43591                 }
43592                 
43593                 return true;
43594             },
43595
43596             "esc" : function(e){
43597                 this.collapse();
43598             },
43599
43600             "tab" : function(e){
43601                 this.collapse();
43602                 
43603                 if(this.fireEvent("specialkey", this, e)){
43604                     this.onViewClick(false);
43605                 }
43606                 
43607                 return true;
43608             },
43609
43610             scope : this,
43611
43612             doRelay : function(foo, bar, hname){
43613                 if(hname == 'down' || this.scope.isExpanded()){
43614                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43615                 }
43616                 return true;
43617             },
43618
43619             forceKeyDown: true
43620         });
43621         
43622         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43623         
43624     },
43625     
43626     initNumberEvent : function(e)
43627     {
43628         this.inputEl().on("keydown" , this.fireKey,  this);
43629         this.inputEl().on("focus", this.onFocus,  this);
43630         this.inputEl().on("blur", this.onBlur,  this);
43631         
43632         this.inputEl().relayEvent('keyup', this);
43633         
43634         if(this.indicator){
43635             this.indicator.addClass('invisible');
43636         }
43637  
43638         this.originalValue = this.getValue();
43639         
43640         if(this.validationEvent == 'keyup'){
43641             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43642             this.inputEl().on('keyup', this.filterValidation, this);
43643         }
43644         else if(this.validationEvent !== false){
43645             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43646         }
43647         
43648         if(this.selectOnFocus){
43649             this.on("focus", this.preFocus, this);
43650             
43651         }
43652         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43653             this.inputEl().on("keypress", this.filterKeys, this);
43654         } else {
43655             this.inputEl().relayEvent('keypress', this);
43656         }
43657         
43658         var allowed = "0123456789";
43659         
43660         if(this.allowDecimals){
43661             allowed += this.decimalSeparator;
43662         }
43663         
43664         if(this.allowNegative){
43665             allowed += "-";
43666         }
43667         
43668         if(this.thousandsDelimiter) {
43669             allowed += ",";
43670         }
43671         
43672         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43673         
43674         var keyPress = function(e){
43675             
43676             var k = e.getKey();
43677             
43678             var c = e.getCharCode();
43679             
43680             if(
43681                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43682                     allowed.indexOf(String.fromCharCode(c)) === -1
43683             ){
43684                 e.stopEvent();
43685                 return;
43686             }
43687             
43688             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43689                 return;
43690             }
43691             
43692             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43693                 e.stopEvent();
43694             }
43695         };
43696         
43697         this.inputEl().on("keypress", keyPress, this);
43698         
43699     },
43700     
43701     onTriggerClick : function(e)
43702     {   
43703         if(this.disabled){
43704             return;
43705         }
43706         
43707         this.page = 0;
43708         this.loadNext = false;
43709         
43710         if(this.isExpanded()){
43711             this.collapse();
43712             return;
43713         }
43714         
43715         this.hasFocus = true;
43716         
43717         if(this.triggerAction == 'all') {
43718             this.doQuery(this.allQuery, true);
43719             return;
43720         }
43721         
43722         this.doQuery(this.getRawValue());
43723     },
43724     
43725     getCurrency : function()
43726     {   
43727         var v = this.currencyEl().getValue();
43728         
43729         return v;
43730     },
43731     
43732     restrictHeight : function()
43733     {
43734         this.list.alignTo(this.currencyEl(), this.listAlign);
43735         this.list.alignTo(this.currencyEl(), this.listAlign);
43736     },
43737     
43738     onViewClick : function(view, doFocus, el, e)
43739     {
43740         var index = this.view.getSelectedIndexes()[0];
43741         
43742         var r = this.store.getAt(index);
43743         
43744         if(r){
43745             this.onSelect(r, index);
43746         }
43747     },
43748     
43749     onSelect : function(record, index){
43750         
43751         if(this.fireEvent('beforeselect', this, record, index) !== false){
43752         
43753             this.setFromCurrencyData(index > -1 ? record.data : false);
43754             
43755             this.collapse();
43756             
43757             this.fireEvent('select', this, record, index);
43758         }
43759     },
43760     
43761     setFromCurrencyData : function(o)
43762     {
43763         var currency = '';
43764         
43765         this.lastCurrency = o;
43766         
43767         if (this.currencyField) {
43768             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43769         } else {
43770             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43771         }
43772         
43773         this.lastSelectionText = currency;
43774         
43775         //setting default currency
43776         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43777             this.setCurrency(this.defaultCurrency);
43778             return;
43779         }
43780         
43781         this.setCurrency(currency);
43782     },
43783     
43784     setFromData : function(o)
43785     {
43786         var c = {};
43787         
43788         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43789         
43790         this.setFromCurrencyData(c);
43791         
43792         var value = '';
43793         
43794         if (this.name) {
43795             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43796         } else {
43797             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43798         }
43799         
43800         this.setValue(value);
43801         
43802     },
43803     
43804     setCurrency : function(v)
43805     {   
43806         this.currencyValue = v;
43807         
43808         if(this.rendered){
43809             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43810             this.validate();
43811         }
43812     },
43813     
43814     setValue : function(v)
43815     {
43816         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43817         
43818         this.value = v;
43819         
43820         if(this.rendered){
43821             
43822             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43823             
43824             this.inputEl().dom.value = (v == '') ? '' :
43825                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43826             
43827             if(!this.allowZero && v === '0') {
43828                 this.hiddenEl().dom.value = '';
43829                 this.inputEl().dom.value = '';
43830             }
43831             
43832             this.validate();
43833         }
43834     },
43835     
43836     getRawValue : function()
43837     {
43838         var v = this.inputEl().getValue();
43839         
43840         return v;
43841     },
43842     
43843     getValue : function()
43844     {
43845         return this.fixPrecision(this.parseValue(this.getRawValue()));
43846     },
43847     
43848     parseValue : function(value)
43849     {
43850         if(this.thousandsDelimiter) {
43851             value += "";
43852             r = new RegExp(",", "g");
43853             value = value.replace(r, "");
43854         }
43855         
43856         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43857         return isNaN(value) ? '' : value;
43858         
43859     },
43860     
43861     fixPrecision : function(value)
43862     {
43863         if(this.thousandsDelimiter) {
43864             value += "";
43865             r = new RegExp(",", "g");
43866             value = value.replace(r, "");
43867         }
43868         
43869         var nan = isNaN(value);
43870         
43871         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43872             return nan ? '' : value;
43873         }
43874         return parseFloat(value).toFixed(this.decimalPrecision);
43875     },
43876     
43877     decimalPrecisionFcn : function(v)
43878     {
43879         return Math.floor(v);
43880     },
43881     
43882     validateValue : function(value)
43883     {
43884         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43885             return false;
43886         }
43887         
43888         var num = this.parseValue(value);
43889         
43890         if(isNaN(num)){
43891             this.markInvalid(String.format(this.nanText, value));
43892             return false;
43893         }
43894         
43895         if(num < this.minValue){
43896             this.markInvalid(String.format(this.minText, this.minValue));
43897             return false;
43898         }
43899         
43900         if(num > this.maxValue){
43901             this.markInvalid(String.format(this.maxText, this.maxValue));
43902             return false;
43903         }
43904         
43905         return true;
43906     },
43907     
43908     validate : function()
43909     {
43910         if(this.disabled || this.allowBlank){
43911             this.markValid();
43912             return true;
43913         }
43914         
43915         var currency = this.getCurrency();
43916         
43917         if(this.validateValue(this.getRawValue()) && currency.length){
43918             this.markValid();
43919             return true;
43920         }
43921         
43922         this.markInvalid();
43923         return false;
43924     },
43925     
43926     getName: function()
43927     {
43928         return this.name;
43929     },
43930     
43931     beforeBlur : function()
43932     {
43933         if(!this.castInt){
43934             return;
43935         }
43936         
43937         var v = this.parseValue(this.getRawValue());
43938         
43939         if(v || v == 0){
43940             this.setValue(v);
43941         }
43942     },
43943     
43944     onBlur : function()
43945     {
43946         this.beforeBlur();
43947         
43948         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43949             //this.el.removeClass(this.focusClass);
43950         }
43951         
43952         this.hasFocus = false;
43953         
43954         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43955             this.validate();
43956         }
43957         
43958         var v = this.getValue();
43959         
43960         if(String(v) !== String(this.startValue)){
43961             this.fireEvent('change', this, v, this.startValue);
43962         }
43963         
43964         this.fireEvent("blur", this);
43965     },
43966     
43967     inputEl : function()
43968     {
43969         return this.el.select('.roo-money-amount-input', true).first();
43970     },
43971     
43972     currencyEl : function()
43973     {
43974         return this.el.select('.roo-money-currency-input', true).first();
43975     },
43976     
43977     hiddenEl : function()
43978     {
43979         return this.el.select('input.hidden-number-input',true).first();
43980     }
43981     
43982 });/**
43983  * @class Roo.bootstrap.BezierSignature
43984  * @extends Roo.bootstrap.Component
43985  * Bootstrap BezierSignature class
43986  * This script refer to:
43987  *    Title: Signature Pad
43988  *    Author: szimek
43989  *    Availability: https://github.com/szimek/signature_pad
43990  *
43991  * @constructor
43992  * Create a new BezierSignature
43993  * @param {Object} config The config object
43994  */
43995
43996 Roo.bootstrap.BezierSignature = function(config){
43997     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43998     this.addEvents({
43999         "resize" : true
44000     });
44001 };
44002
44003 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44004 {
44005      
44006     curve_data: [],
44007     
44008     is_empty: true,
44009     
44010     mouse_btn_down: true,
44011     
44012     /**
44013      * @cfg {int} canvas height
44014      */
44015     canvas_height: '200px',
44016     
44017     /**
44018      * @cfg {float|function} Radius of a single dot.
44019      */ 
44020     dot_size: false,
44021     
44022     /**
44023      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44024      */
44025     min_width: 0.5,
44026     
44027     /**
44028      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44029      */
44030     max_width: 2.5,
44031     
44032     /**
44033      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44034      */
44035     throttle: 16,
44036     
44037     /**
44038      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44039      */
44040     min_distance: 5,
44041     
44042     /**
44043      * @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.
44044      */
44045     bg_color: 'rgba(0, 0, 0, 0)',
44046     
44047     /**
44048      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44049      */
44050     dot_color: 'black',
44051     
44052     /**
44053      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44054      */ 
44055     velocity_filter_weight: 0.7,
44056     
44057     /**
44058      * @cfg {function} Callback when stroke begin. 
44059      */
44060     onBegin: false,
44061     
44062     /**
44063      * @cfg {function} Callback when stroke end.
44064      */
44065     onEnd: false,
44066     
44067     getAutoCreate : function()
44068     {
44069         var cls = 'roo-signature column';
44070         
44071         if(this.cls){
44072             cls += ' ' + this.cls;
44073         }
44074         
44075         var col_sizes = [
44076             'lg',
44077             'md',
44078             'sm',
44079             'xs'
44080         ];
44081         
44082         for(var i = 0; i < col_sizes.length; i++) {
44083             if(this[col_sizes[i]]) {
44084                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44085             }
44086         }
44087         
44088         var cfg = {
44089             tag: 'div',
44090             cls: cls,
44091             cn: [
44092                 {
44093                     tag: 'div',
44094                     cls: 'roo-signature-body',
44095                     cn: [
44096                         {
44097                             tag: 'canvas',
44098                             cls: 'roo-signature-body-canvas',
44099                             height: this.canvas_height,
44100                             width: this.canvas_width
44101                         }
44102                     ]
44103                 },
44104                 {
44105                     tag: 'input',
44106                     type: 'file',
44107                     style: 'display: none'
44108                 }
44109             ]
44110         };
44111         
44112         return cfg;
44113     },
44114     
44115     initEvents: function() 
44116     {
44117         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44118         
44119         var canvas = this.canvasEl();
44120         
44121         // mouse && touch event swapping...
44122         canvas.dom.style.touchAction = 'none';
44123         canvas.dom.style.msTouchAction = 'none';
44124         
44125         this.mouse_btn_down = false;
44126         canvas.on('mousedown', this._handleMouseDown, this);
44127         canvas.on('mousemove', this._handleMouseMove, this);
44128         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44129         
44130         if (window.PointerEvent) {
44131             canvas.on('pointerdown', this._handleMouseDown, this);
44132             canvas.on('pointermove', this._handleMouseMove, this);
44133             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44134         }
44135         
44136         if ('ontouchstart' in window) {
44137             canvas.on('touchstart', this._handleTouchStart, this);
44138             canvas.on('touchmove', this._handleTouchMove, this);
44139             canvas.on('touchend', this._handleTouchEnd, this);
44140         }
44141         
44142         Roo.EventManager.onWindowResize(this.resize, this, true);
44143         
44144         // file input event
44145         this.fileEl().on('change', this.uploadImage, this);
44146         
44147         this.clear();
44148         
44149         this.resize();
44150     },
44151     
44152     resize: function(){
44153         
44154         var canvas = this.canvasEl().dom;
44155         var ctx = this.canvasElCtx();
44156         var img_data = false;
44157         
44158         if(canvas.width > 0) {
44159             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44160         }
44161         // setting canvas width will clean img data
44162         canvas.width = 0;
44163         
44164         var style = window.getComputedStyle ? 
44165             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44166             
44167         var padding_left = parseInt(style.paddingLeft) || 0;
44168         var padding_right = parseInt(style.paddingRight) || 0;
44169         
44170         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44171         
44172         if(img_data) {
44173             ctx.putImageData(img_data, 0, 0);
44174         }
44175     },
44176     
44177     _handleMouseDown: function(e)
44178     {
44179         if (e.browserEvent.which === 1) {
44180             this.mouse_btn_down = true;
44181             this.strokeBegin(e);
44182         }
44183     },
44184     
44185     _handleMouseMove: function (e)
44186     {
44187         if (this.mouse_btn_down) {
44188             this.strokeMoveUpdate(e);
44189         }
44190     },
44191     
44192     _handleMouseUp: function (e)
44193     {
44194         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44195             this.mouse_btn_down = false;
44196             this.strokeEnd(e);
44197         }
44198     },
44199     
44200     _handleTouchStart: function (e) {
44201         
44202         e.preventDefault();
44203         if (e.browserEvent.targetTouches.length === 1) {
44204             // var touch = e.browserEvent.changedTouches[0];
44205             // this.strokeBegin(touch);
44206             
44207              this.strokeBegin(e); // assume e catching the correct xy...
44208         }
44209     },
44210     
44211     _handleTouchMove: function (e) {
44212         e.preventDefault();
44213         // var touch = event.targetTouches[0];
44214         // _this._strokeMoveUpdate(touch);
44215         this.strokeMoveUpdate(e);
44216     },
44217     
44218     _handleTouchEnd: function (e) {
44219         var wasCanvasTouched = e.target === this.canvasEl().dom;
44220         if (wasCanvasTouched) {
44221             e.preventDefault();
44222             // var touch = event.changedTouches[0];
44223             // _this._strokeEnd(touch);
44224             this.strokeEnd(e);
44225         }
44226     },
44227     
44228     reset: function () {
44229         this._lastPoints = [];
44230         this._lastVelocity = 0;
44231         this._lastWidth = (this.min_width + this.max_width) / 2;
44232         this.canvasElCtx().fillStyle = this.dot_color;
44233     },
44234     
44235     strokeMoveUpdate: function(e)
44236     {
44237         this.strokeUpdate(e);
44238         
44239         if (this.throttle) {
44240             this.throttleStroke(this.strokeUpdate, this.throttle);
44241         }
44242         else {
44243             this.strokeUpdate(e);
44244         }
44245     },
44246     
44247     strokeBegin: function(e)
44248     {
44249         var newPointGroup = {
44250             color: this.dot_color,
44251             points: []
44252         };
44253         
44254         if (typeof this.onBegin === 'function') {
44255             this.onBegin(e);
44256         }
44257         
44258         this.curve_data.push(newPointGroup);
44259         this.reset();
44260         this.strokeUpdate(e);
44261     },
44262     
44263     strokeUpdate: function(e)
44264     {
44265         var rect = this.canvasEl().dom.getBoundingClientRect();
44266         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44267         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44268         var lastPoints = lastPointGroup.points;
44269         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44270         var isLastPointTooClose = lastPoint
44271             ? point.distanceTo(lastPoint) <= this.min_distance
44272             : false;
44273         var color = lastPointGroup.color;
44274         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44275             var curve = this.addPoint(point);
44276             if (!lastPoint) {
44277                 this.drawDot({color: color, point: point});
44278             }
44279             else if (curve) {
44280                 this.drawCurve({color: color, curve: curve});
44281             }
44282             lastPoints.push({
44283                 time: point.time,
44284                 x: point.x,
44285                 y: point.y
44286             });
44287         }
44288     },
44289     
44290     strokeEnd: function(e)
44291     {
44292         this.strokeUpdate(e);
44293         if (typeof this.onEnd === 'function') {
44294             this.onEnd(e);
44295         }
44296     },
44297     
44298     addPoint:  function (point) {
44299         var _lastPoints = this._lastPoints;
44300         _lastPoints.push(point);
44301         if (_lastPoints.length > 2) {
44302             if (_lastPoints.length === 3) {
44303                 _lastPoints.unshift(_lastPoints[0]);
44304             }
44305             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44306             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44307             _lastPoints.shift();
44308             return curve;
44309         }
44310         return null;
44311     },
44312     
44313     calculateCurveWidths: function (startPoint, endPoint) {
44314         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44315             (1 - this.velocity_filter_weight) * this._lastVelocity;
44316
44317         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44318         var widths = {
44319             end: newWidth,
44320             start: this._lastWidth
44321         };
44322         
44323         this._lastVelocity = velocity;
44324         this._lastWidth = newWidth;
44325         return widths;
44326     },
44327     
44328     drawDot: function (_a) {
44329         var color = _a.color, point = _a.point;
44330         var ctx = this.canvasElCtx();
44331         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44332         ctx.beginPath();
44333         this.drawCurveSegment(point.x, point.y, width);
44334         ctx.closePath();
44335         ctx.fillStyle = color;
44336         ctx.fill();
44337     },
44338     
44339     drawCurve: function (_a) {
44340         var color = _a.color, curve = _a.curve;
44341         var ctx = this.canvasElCtx();
44342         var widthDelta = curve.endWidth - curve.startWidth;
44343         var drawSteps = Math.floor(curve.length()) * 2;
44344         ctx.beginPath();
44345         ctx.fillStyle = color;
44346         for (var i = 0; i < drawSteps; i += 1) {
44347         var t = i / drawSteps;
44348         var tt = t * t;
44349         var ttt = tt * t;
44350         var u = 1 - t;
44351         var uu = u * u;
44352         var uuu = uu * u;
44353         var x = uuu * curve.startPoint.x;
44354         x += 3 * uu * t * curve.control1.x;
44355         x += 3 * u * tt * curve.control2.x;
44356         x += ttt * curve.endPoint.x;
44357         var y = uuu * curve.startPoint.y;
44358         y += 3 * uu * t * curve.control1.y;
44359         y += 3 * u * tt * curve.control2.y;
44360         y += ttt * curve.endPoint.y;
44361         var width = curve.startWidth + ttt * widthDelta;
44362         this.drawCurveSegment(x, y, width);
44363         }
44364         ctx.closePath();
44365         ctx.fill();
44366     },
44367     
44368     drawCurveSegment: function (x, y, width) {
44369         var ctx = this.canvasElCtx();
44370         ctx.moveTo(x, y);
44371         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44372         this.is_empty = false;
44373     },
44374     
44375     clear: function()
44376     {
44377         var ctx = this.canvasElCtx();
44378         var canvas = this.canvasEl().dom;
44379         ctx.fillStyle = this.bg_color;
44380         ctx.clearRect(0, 0, canvas.width, canvas.height);
44381         ctx.fillRect(0, 0, canvas.width, canvas.height);
44382         this.curve_data = [];
44383         this.reset();
44384         this.is_empty = true;
44385     },
44386     
44387     fileEl: function()
44388     {
44389         return  this.el.select('input',true).first();
44390     },
44391     
44392     canvasEl: function()
44393     {
44394         return this.el.select('canvas',true).first();
44395     },
44396     
44397     canvasElCtx: function()
44398     {
44399         return this.el.select('canvas',true).first().dom.getContext('2d');
44400     },
44401     
44402     getImage: function(type)
44403     {
44404         if(this.is_empty) {
44405             return false;
44406         }
44407         
44408         // encryption ?
44409         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44410     },
44411     
44412     drawFromImage: function(img_src)
44413     {
44414         var img = new Image();
44415         
44416         img.onload = function(){
44417             this.canvasElCtx().drawImage(img, 0, 0);
44418         }.bind(this);
44419         
44420         img.src = img_src;
44421         
44422         this.is_empty = false;
44423     },
44424     
44425     selectImage: function()
44426     {
44427         this.fileEl().dom.click();
44428     },
44429     
44430     uploadImage: function(e)
44431     {
44432         var reader = new FileReader();
44433         
44434         reader.onload = function(e){
44435             var img = new Image();
44436             img.onload = function(){
44437                 this.reset();
44438                 this.canvasElCtx().drawImage(img, 0, 0);
44439             }.bind(this);
44440             img.src = e.target.result;
44441         }.bind(this);
44442         
44443         reader.readAsDataURL(e.target.files[0]);
44444     },
44445     
44446     // Bezier Point Constructor
44447     Point: (function () {
44448         function Point(x, y, time) {
44449             this.x = x;
44450             this.y = y;
44451             this.time = time || Date.now();
44452         }
44453         Point.prototype.distanceTo = function (start) {
44454             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44455         };
44456         Point.prototype.equals = function (other) {
44457             return this.x === other.x && this.y === other.y && this.time === other.time;
44458         };
44459         Point.prototype.velocityFrom = function (start) {
44460             return this.time !== start.time
44461             ? this.distanceTo(start) / (this.time - start.time)
44462             : 0;
44463         };
44464         return Point;
44465     }()),
44466     
44467     
44468     // Bezier Constructor
44469     Bezier: (function () {
44470         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44471             this.startPoint = startPoint;
44472             this.control2 = control2;
44473             this.control1 = control1;
44474             this.endPoint = endPoint;
44475             this.startWidth = startWidth;
44476             this.endWidth = endWidth;
44477         }
44478         Bezier.fromPoints = function (points, widths, scope) {
44479             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44480             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44481             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44482         };
44483         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44484             var dx1 = s1.x - s2.x;
44485             var dy1 = s1.y - s2.y;
44486             var dx2 = s2.x - s3.x;
44487             var dy2 = s2.y - s3.y;
44488             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44489             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44490             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44491             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44492             var dxm = m1.x - m2.x;
44493             var dym = m1.y - m2.y;
44494             var k = l2 / (l1 + l2);
44495             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44496             var tx = s2.x - cm.x;
44497             var ty = s2.y - cm.y;
44498             return {
44499                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44500                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44501             };
44502         };
44503         Bezier.prototype.length = function () {
44504             var steps = 10;
44505             var length = 0;
44506             var px;
44507             var py;
44508             for (var i = 0; i <= steps; i += 1) {
44509                 var t = i / steps;
44510                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44511                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44512                 if (i > 0) {
44513                     var xdiff = cx - px;
44514                     var ydiff = cy - py;
44515                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44516                 }
44517                 px = cx;
44518                 py = cy;
44519             }
44520             return length;
44521         };
44522         Bezier.prototype.point = function (t, start, c1, c2, end) {
44523             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44524             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44525             + (3.0 * c2 * (1.0 - t) * t * t)
44526             + (end * t * t * t);
44527         };
44528         return Bezier;
44529     }()),
44530     
44531     throttleStroke: function(fn, wait) {
44532       if (wait === void 0) { wait = 250; }
44533       var previous = 0;
44534       var timeout = null;
44535       var result;
44536       var storedContext;
44537       var storedArgs;
44538       var later = function () {
44539           previous = Date.now();
44540           timeout = null;
44541           result = fn.apply(storedContext, storedArgs);
44542           if (!timeout) {
44543               storedContext = null;
44544               storedArgs = [];
44545           }
44546       };
44547       return function wrapper() {
44548           var args = [];
44549           for (var _i = 0; _i < arguments.length; _i++) {
44550               args[_i] = arguments[_i];
44551           }
44552           var now = Date.now();
44553           var remaining = wait - (now - previous);
44554           storedContext = this;
44555           storedArgs = args;
44556           if (remaining <= 0 || remaining > wait) {
44557               if (timeout) {
44558                   clearTimeout(timeout);
44559                   timeout = null;
44560               }
44561               previous = now;
44562               result = fn.apply(storedContext, storedArgs);
44563               if (!timeout) {
44564                   storedContext = null;
44565                   storedArgs = [];
44566               }
44567           }
44568           else if (!timeout) {
44569               timeout = window.setTimeout(later, remaining);
44570           }
44571           return result;
44572       };
44573   }
44574   
44575 });
44576
44577  
44578
44579