sync
[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  * @cfg {String} role default blank - set to button to force cursor pointer
654  
655  * 
656  * @constructor
657  * Create a new Element
658  * @param {Object} config The config object
659  */
660
661 Roo.bootstrap.Element = function(config){
662     Roo.bootstrap.Element.superclass.constructor.call(this, config);
663     
664     this.addEvents({
665         // raw events
666         /**
667          * @event click
668          * When a element is chick
669          * @param {Roo.bootstrap.Element} this
670          * @param {Roo.EventObject} e
671          */
672         "click" : true 
673         
674       
675     });
676 };
677
678 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
679     
680     tag: 'div',
681     cls: '',
682     html: '',
683     preventDefault: false, 
684     clickable: false,
685     tapedTwice : false,
686     role : false,
687     
688     getAutoCreate : function(){
689         
690         var cfg = {
691             tag: this.tag,
692             // cls: this.cls, double assign in parent class Component.js :: onRender
693             html: this.html
694         };
695         if (this.role !== false) {
696             cfg.role = this.role;
697         }
698         
699         return cfg;
700     },
701     
702     initEvents: function() 
703     {
704         Roo.bootstrap.Element.superclass.initEvents.call(this);
705         
706         if(this.clickable){
707             this.el.on('click', this.onClick, this);
708         }
709         
710         
711     },
712     
713     onClick : function(e)
714     {
715         if(this.preventDefault){
716             e.preventDefault();
717         }
718         
719         this.fireEvent('click', this, e); // why was this double click before?
720     },
721     
722     
723     
724
725     
726     
727     getValue : function()
728     {
729         return this.el.dom.innerHTML;
730     },
731     
732     setValue : function(value)
733     {
734         this.el.dom.innerHTML = value;
735     }
736    
737 });
738
739  
740
741  /*
742  * - LGPL
743  *
744  * dropable area
745  * 
746  */
747
748 /**
749  * @class Roo.bootstrap.DropTarget
750  * @extends Roo.bootstrap.Element
751  * Bootstrap DropTarget class
752  
753  * @cfg {string} name dropable name
754  * 
755  * @constructor
756  * Create a new Dropable Area
757  * @param {Object} config The config object
758  */
759
760 Roo.bootstrap.DropTarget = function(config){
761     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
762     
763     this.addEvents({
764         // raw events
765         /**
766          * @event click
767          * When a element is chick
768          * @param {Roo.bootstrap.Element} this
769          * @param {Roo.EventObject} e
770          */
771         "drop" : true
772     });
773 };
774
775 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
776     
777     
778     getAutoCreate : function(){
779         
780          
781     },
782     
783     initEvents: function() 
784     {
785         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
786         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
787             ddGroup: this.name,
788             listeners : {
789                 drop : this.dragDrop.createDelegate(this),
790                 enter : this.dragEnter.createDelegate(this),
791                 out : this.dragOut.createDelegate(this),
792                 over : this.dragOver.createDelegate(this)
793             }
794             
795         });
796         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
797     },
798     
799     dragDrop : function(source,e,data)
800     {
801         // user has to decide how to impliment this.
802         Roo.log('drop');
803         Roo.log(this);
804         //this.fireEvent('drop', this, source, e ,data);
805         return false;
806     },
807     
808     dragEnter : function(n, dd, e, data)
809     {
810         // probably want to resize the element to match the dropped element..
811         Roo.log("enter");
812         this.originalSize = this.el.getSize();
813         this.el.setSize( n.el.getSize());
814         this.dropZone.DDM.refreshCache(this.name);
815         Roo.log([n, dd, e, data]);
816     },
817     
818     dragOut : function(value)
819     {
820         // resize back to normal
821         Roo.log("out");
822         this.el.setSize(this.originalSize);
823         this.dropZone.resetConstraints();
824     },
825     
826     dragOver : function()
827     {
828         // ??? do nothing?
829     }
830    
831 });
832
833  
834
835  /*
836  * - LGPL
837  *
838  * Body
839  *
840  */
841
842 /**
843  * @class Roo.bootstrap.Body
844  * @extends Roo.bootstrap.Component
845  * Bootstrap Body class
846  *
847  * @constructor
848  * Create a new body
849  * @param {Object} config The config object
850  */
851
852 Roo.bootstrap.Body = function(config){
853
854     config = config || {};
855
856     Roo.bootstrap.Body.superclass.constructor.call(this, config);
857     this.el = Roo.get(config.el ? config.el : document.body );
858     if (this.cls && this.cls.length) {
859         Roo.get(document.body).addClass(this.cls);
860     }
861 };
862
863 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
864
865     is_body : true,// just to make sure it's constructed?
866
867         autoCreate : {
868         cls: 'container'
869     },
870     onRender : function(ct, position)
871     {
872        /* Roo.log("Roo.bootstrap.Body - onRender");
873         if (this.cls && this.cls.length) {
874             Roo.get(document.body).addClass(this.cls);
875         }
876         // style??? xttr???
877         */
878     }
879
880
881
882
883 });
884 /*
885  * - LGPL
886  *
887  * button group
888  * 
889  */
890
891
892 /**
893  * @class Roo.bootstrap.ButtonGroup
894  * @extends Roo.bootstrap.Component
895  * Bootstrap ButtonGroup class
896  * @cfg {String} size lg | sm | xs (default empty normal)
897  * @cfg {String} align vertical | justified  (default none)
898  * @cfg {String} direction up | down (default down)
899  * @cfg {Boolean} toolbar false | true
900  * @cfg {Boolean} btn true | false
901  * 
902  * 
903  * @constructor
904  * Create a new Input
905  * @param {Object} config The config object
906  */
907
908 Roo.bootstrap.ButtonGroup = function(config){
909     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
910 };
911
912 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
913     
914     size: '',
915     align: '',
916     direction: '',
917     toolbar: false,
918     btn: true,
919
920     getAutoCreate : function(){
921         var cfg = {
922             cls: 'btn-group',
923             html : null
924         };
925         
926         cfg.html = this.html || cfg.html;
927         
928         if (this.toolbar) {
929             cfg = {
930                 cls: 'btn-toolbar',
931                 html: null
932             };
933             
934             return cfg;
935         }
936         
937         if (['vertical','justified'].indexOf(this.align)!==-1) {
938             cfg.cls = 'btn-group-' + this.align;
939             
940             if (this.align == 'justified') {
941                 console.log(this.items);
942             }
943         }
944         
945         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
946             cfg.cls += ' btn-group-' + this.size;
947         }
948         
949         if (this.direction == 'up') {
950             cfg.cls += ' dropup' ;
951         }
952         
953         return cfg;
954     },
955     /**
956      * Add a button to the group (similar to NavItem API.)
957      */
958     addItem : function(cfg)
959     {
960         var cn = new Roo.bootstrap.Button(cfg);
961         //this.register(cn);
962         cn.parentId = this.id;
963         cn.onRender(this.el, null);
964         return cn;
965     }
966    
967 });
968
969  /*
970  * - LGPL
971  *
972  * button
973  * 
974  */
975
976 /**
977  * @class Roo.bootstrap.Button
978  * @extends Roo.bootstrap.Component
979  * Bootstrap Button class
980  * @cfg {String} html The button content
981  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
982  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
983  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
984  * @cfg {String} size (lg|sm|xs)
985  * @cfg {String} tag (a|input|submit)
986  * @cfg {String} href empty or href
987  * @cfg {Boolean} disabled default false;
988  * @cfg {Boolean} isClose default false;
989  * @cfg {String} glyphicon depricated - use fa
990  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
991  * @cfg {String} badge text for badge
992  * @cfg {String} theme (default|glow)  
993  * @cfg {Boolean} inverse dark themed version
994  * @cfg {Boolean} toggle is it a slidy toggle button
995  * @cfg {Boolean} pressed   default null - if the button ahs active state
996  * @cfg {String} ontext text for on slidy toggle state
997  * @cfg {String} offtext text for off slidy toggle state
998  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
999  * @cfg {Boolean} removeClass remove the standard class..
1000  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1001  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1002  * 
1003  * @constructor
1004  * Create a new button
1005  * @param {Object} config The config object
1006  */
1007
1008
1009 Roo.bootstrap.Button = function(config){
1010     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1011     
1012     this.addEvents({
1013         // raw events
1014         /**
1015          * @event click
1016          * When a button is pressed
1017          * @param {Roo.bootstrap.Button} btn
1018          * @param {Roo.EventObject} e
1019          */
1020         "click" : true,
1021         /**
1022          * @event dblclick
1023          * When a button is double clicked
1024          * @param {Roo.bootstrap.Button} btn
1025          * @param {Roo.EventObject} e
1026          */
1027         "dblclick" : true,
1028          /**
1029          * @event toggle
1030          * After the button has been toggles
1031          * @param {Roo.bootstrap.Button} btn
1032          * @param {Roo.EventObject} e
1033          * @param {boolean} pressed (also available as button.pressed)
1034          */
1035         "toggle" : true
1036     });
1037 };
1038
1039 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1040     html: false,
1041     active: false,
1042     weight: '',
1043     badge_weight: '',
1044     outline : false,
1045     size: '',
1046     tag: 'button',
1047     href: '',
1048     disabled: false,
1049     isClose: false,
1050     glyphicon: '',
1051     fa: '',
1052     badge: '',
1053     theme: 'default',
1054     inverse: false,
1055     
1056     toggle: false,
1057     ontext: 'ON',
1058     offtext: 'OFF',
1059     defaulton: true,
1060     preventDefault: true,
1061     removeClass: false,
1062     name: false,
1063     target: false,
1064     group : false,
1065      
1066     pressed : null,
1067      
1068     
1069     getAutoCreate : function(){
1070         
1071         var cfg = {
1072             tag : 'button',
1073             cls : 'roo-button',
1074             html: ''
1075         };
1076         
1077         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1078             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1079             this.tag = 'button';
1080         } else {
1081             cfg.tag = this.tag;
1082         }
1083         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1084         
1085         if (this.toggle == true) {
1086             cfg={
1087                 tag: 'div',
1088                 cls: 'slider-frame roo-button',
1089                 cn: [
1090                     {
1091                         tag: 'span',
1092                         'data-on-text':'ON',
1093                         'data-off-text':'OFF',
1094                         cls: 'slider-button',
1095                         html: this.offtext
1096                     }
1097                 ]
1098             };
1099             // why are we validating the weights?
1100             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1101                 cfg.cls +=  ' ' + this.weight;
1102             }
1103             
1104             return cfg;
1105         }
1106         
1107         if (this.isClose) {
1108             cfg.cls += ' close';
1109             
1110             cfg["aria-hidden"] = true;
1111             
1112             cfg.html = "&times;";
1113             
1114             return cfg;
1115         }
1116              
1117         
1118         if (this.theme==='default') {
1119             cfg.cls = 'btn roo-button';
1120             
1121             //if (this.parentType != 'Navbar') {
1122             this.weight = this.weight.length ?  this.weight : 'default';
1123             //}
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1127                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1128                 cfg.cls += ' btn-' + outline + weight;
1129                 if (this.weight == 'default') {
1130                     // BC
1131                     cfg.cls += ' btn-' + this.weight;
1132                 }
1133             }
1134         } else if (this.theme==='glow') {
1135             
1136             cfg.tag = 'a';
1137             cfg.cls = 'btn-glow roo-button';
1138             
1139             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1140                 
1141                 cfg.cls += ' ' + this.weight;
1142             }
1143         }
1144    
1145         
1146         if (this.inverse) {
1147             this.cls += ' inverse';
1148         }
1149         
1150         
1151         if (this.active || this.pressed === true) {
1152             cfg.cls += ' active';
1153         }
1154         
1155         if (this.disabled) {
1156             cfg.disabled = 'disabled';
1157         }
1158         
1159         if (this.items) {
1160             Roo.log('changing to ul' );
1161             cfg.tag = 'ul';
1162             this.glyphicon = 'caret';
1163             if (Roo.bootstrap.version == 4) {
1164                 this.fa = 'caret-down';
1165             }
1166             
1167         }
1168         
1169         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1170          
1171         //gsRoo.log(this.parentType);
1172         if (this.parentType === 'Navbar' && !this.parent().bar) {
1173             Roo.log('changing to li?');
1174             
1175             cfg.tag = 'li';
1176             
1177             cfg.cls = '';
1178             cfg.cn =  [{
1179                 tag : 'a',
1180                 cls : 'roo-button',
1181                 html : this.html,
1182                 href : this.href || '#'
1183             }];
1184             if (this.menu) {
1185                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1186                 cfg.cls += ' dropdown';
1187             }   
1188             
1189             delete cfg.html;
1190             
1191         }
1192         
1193        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1194         
1195         if (this.glyphicon) {
1196             cfg.html = ' ' + cfg.html;
1197             
1198             cfg.cn = [
1199                 {
1200                     tag: 'span',
1201                     cls: 'glyphicon glyphicon-' + this.glyphicon
1202                 }
1203             ];
1204         }
1205         if (this.fa) {
1206             cfg.html = ' ' + cfg.html;
1207             
1208             cfg.cn = [
1209                 {
1210                     tag: 'i',
1211                     cls: 'fa fas fa-' + this.fa
1212                 }
1213             ];
1214         }
1215         
1216         if (this.badge) {
1217             cfg.html += ' ';
1218             
1219             cfg.tag = 'a';
1220             
1221 //            cfg.cls='btn roo-button';
1222             
1223             cfg.href=this.href;
1224             
1225             var value = cfg.html;
1226             
1227             if(this.glyphicon){
1228                 value = {
1229                     tag: 'span',
1230                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1231                     html: this.html
1232                 };
1233             }
1234             if(this.fa){
1235                 value = {
1236                     tag: 'i',
1237                     cls: 'fa fas fa-' + this.fa,
1238                     html: this.html
1239                 };
1240             }
1241             
1242             var bw = this.badge_weight.length ? this.badge_weight :
1243                 (this.weight.length ? this.weight : 'secondary');
1244             bw = bw == 'default' ? 'secondary' : bw;
1245             
1246             cfg.cn = [
1247                 value,
1248                 {
1249                     tag: 'span',
1250                     cls: 'badge badge-' + bw,
1251                     html: this.badge
1252                 }
1253             ];
1254             
1255             cfg.html='';
1256         }
1257         
1258         if (this.menu) {
1259             cfg.cls += ' dropdown';
1260             cfg.html = typeof(cfg.html) != 'undefined' ?
1261                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1262         }
1263         
1264         if (cfg.tag !== 'a' && this.href !== '') {
1265             throw "Tag must be a to set href.";
1266         } else if (this.href.length > 0) {
1267             cfg.href = this.href;
1268         }
1269         
1270         if(this.removeClass){
1271             cfg.cls = '';
1272         }
1273         
1274         if(this.target){
1275             cfg.target = this.target;
1276         }
1277         
1278         return cfg;
1279     },
1280     initEvents: function() {
1281        // Roo.log('init events?');
1282 //        Roo.log(this.el.dom);
1283         // add the menu...
1284         
1285         if (typeof (this.menu) != 'undefined') {
1286             this.menu.parentType = this.xtype;
1287             this.menu.triggerEl = this.el;
1288             this.addxtype(Roo.apply({}, this.menu));
1289         }
1290
1291
1292         if (this.el.hasClass('roo-button')) {
1293              this.el.on('click', this.onClick, this);
1294              this.el.on('dblclick', this.onDblClick, this);
1295         } else {
1296              this.el.select('.roo-button').on('click', this.onClick, this);
1297              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1298              
1299         }
1300         // why?
1301         if(this.removeClass){
1302             this.el.on('click', this.onClick, this);
1303         }
1304         
1305         if (this.group === true) {
1306              if (this.pressed === false || this.pressed === true) {
1307                 // nothing
1308             } else {
1309                 this.pressed = false;
1310                 this.setActive(this.pressed);
1311             }
1312             
1313         }
1314         
1315         this.el.enableDisplayMode();
1316         
1317     },
1318     onClick : function(e)
1319     {
1320         if (this.disabled) {
1321             return;
1322         }
1323         
1324         Roo.log('button on click ');
1325         if(this.preventDefault){
1326             e.preventDefault();
1327         }
1328         
1329         if (this.group) {
1330             if (this.pressed) {
1331                 // do nothing -
1332                 return;
1333             }
1334             this.setActive(true);
1335             var pi = this.parent().items;
1336             for (var i = 0;i < pi.length;i++) {
1337                 if (this == pi[i]) {
1338                     continue;
1339                 }
1340                 if (pi[i].el.hasClass('roo-button')) {
1341                     pi[i].setActive(false);
1342                 }
1343             }
1344             this.fireEvent('click', this, e);            
1345             return;
1346         }
1347         
1348         if (this.pressed === true || this.pressed === false) {
1349             this.toggleActive(e);
1350         }
1351         
1352         
1353         this.fireEvent('click', this, e);
1354     },
1355     onDblClick: function(e)
1356     {
1357         if (this.disabled) {
1358             return;
1359         }
1360         if(this.preventDefault){
1361             e.preventDefault();
1362         }
1363         this.fireEvent('dblclick', this, e);
1364     },
1365     /**
1366      * Enables this button
1367      */
1368     enable : function()
1369     {
1370         this.disabled = false;
1371         this.el.removeClass('disabled');
1372         this.el.dom.removeAttribute("disabled");
1373     },
1374     
1375     /**
1376      * Disable this button
1377      */
1378     disable : function()
1379     {
1380         this.disabled = true;
1381         this.el.addClass('disabled');
1382         this.el.attr("disabled", "disabled")
1383     },
1384      /**
1385      * sets the active state on/off, 
1386      * @param {Boolean} state (optional) Force a particular state
1387      */
1388     setActive : function(v) {
1389         
1390         this.el[v ? 'addClass' : 'removeClass']('active');
1391         this.pressed = v;
1392     },
1393      /**
1394      * toggles the current active state 
1395      */
1396     toggleActive : function(e)
1397     {
1398         this.setActive(!this.pressed); // this modifies pressed...
1399         this.fireEvent('toggle', this, e, this.pressed);
1400     },
1401      /**
1402      * get the current active state
1403      * @return {boolean} true if it's active
1404      */
1405     isActive : function()
1406     {
1407         return this.el.hasClass('active');
1408     },
1409     /**
1410      * set the text of the first selected button
1411      */
1412     setText : function(str)
1413     {
1414         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1415     },
1416     /**
1417      * get the text of the first selected button
1418      */
1419     getText : function()
1420     {
1421         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1422     },
1423     
1424     setWeight : function(str)
1425     {
1426         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1427         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1428         this.weight = str;
1429         var outline = this.outline ? 'outline-' : '';
1430         if (str == 'default') {
1431             this.el.addClass('btn-default btn-outline-secondary');        
1432             return;
1433         }
1434         this.el.addClass('btn-' + outline + str);        
1435     }
1436     
1437     
1438 });
1439 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1440
1441 Roo.bootstrap.Button.weights = [
1442     'default',
1443     'secondary' ,
1444     'primary',
1445     'success',
1446     'info',
1447     'warning',
1448     'danger',
1449     'link',
1450     'light',
1451     'dark'              
1452    
1453 ];/*
1454  * - LGPL
1455  *
1456  * column
1457  * 
1458  */
1459
1460 /**
1461  * @class Roo.bootstrap.Column
1462  * @extends Roo.bootstrap.Component
1463  * Bootstrap Column class
1464  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1465  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1466  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1467  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1468  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1469  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1470  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1471  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1472  *
1473  * 
1474  * @cfg {Boolean} hidden (true|false) hide the element
1475  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1476  * @cfg {String} fa (ban|check|...) font awesome icon
1477  * @cfg {Number} fasize (1|2|....) font awsome size
1478
1479  * @cfg {String} icon (info-sign|check|...) glyphicon name
1480
1481  * @cfg {String} html content of column.
1482  * 
1483  * @constructor
1484  * Create a new Column
1485  * @param {Object} config The config object
1486  */
1487
1488 Roo.bootstrap.Column = function(config){
1489     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1490 };
1491
1492 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1493     
1494     xs: false,
1495     sm: false,
1496     md: false,
1497     lg: false,
1498     xsoff: false,
1499     smoff: false,
1500     mdoff: false,
1501     lgoff: false,
1502     html: '',
1503     offset: 0,
1504     alert: false,
1505     fa: false,
1506     icon : false,
1507     hidden : false,
1508     fasize : 1,
1509     
1510     getAutoCreate : function(){
1511         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1512         
1513         cfg = {
1514             tag: 'div',
1515             cls: 'column'
1516         };
1517         
1518         var settings=this;
1519         var sizes =   ['xs','sm','md','lg'];
1520         sizes.map(function(size ,ix){
1521             //Roo.log( size + ':' + settings[size]);
1522             
1523             if (settings[size+'off'] !== false) {
1524                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1525             }
1526             
1527             if (settings[size] === false) {
1528                 return;
1529             }
1530             
1531             if (!settings[size]) { // 0 = hidden
1532                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1533                 // bootsrap4
1534                 for (var i = ix; i > -1; i--) {
1535                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1536                 }
1537                 
1538                 
1539                 return;
1540             }
1541             cfg.cls += ' col-' + size + '-' + settings[size] + (
1542                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1543             );
1544             
1545         });
1546         
1547         if (this.hidden) {
1548             cfg.cls += ' hidden';
1549         }
1550         
1551         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1552             cfg.cls +=' alert alert-' + this.alert;
1553         }
1554         
1555         
1556         if (this.html.length) {
1557             cfg.html = this.html;
1558         }
1559         if (this.fa) {
1560             var fasize = '';
1561             if (this.fasize > 1) {
1562                 fasize = ' fa-' + this.fasize + 'x';
1563             }
1564             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1565             
1566             
1567         }
1568         if (this.icon) {
1569             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1570         }
1571         
1572         return cfg;
1573     }
1574    
1575 });
1576
1577  
1578
1579  /*
1580  * - LGPL
1581  *
1582  * page container.
1583  * 
1584  */
1585
1586
1587 /**
1588  * @class Roo.bootstrap.Container
1589  * @extends Roo.bootstrap.Component
1590  * Bootstrap Container class
1591  * @cfg {Boolean} jumbotron is it a jumbotron element
1592  * @cfg {String} html content of element
1593  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1594  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1595  * @cfg {String} header content of header (for panel)
1596  * @cfg {String} footer content of footer (for panel)
1597  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1598  * @cfg {String} tag (header|aside|section) type of HTML tag.
1599  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1600  * @cfg {String} fa font awesome icon
1601  * @cfg {String} icon (info-sign|check|...) glyphicon name
1602  * @cfg {Boolean} hidden (true|false) hide the element
1603  * @cfg {Boolean} expandable (true|false) default false
1604  * @cfg {Boolean} expanded (true|false) default true
1605  * @cfg {String} rheader contet on the right of header
1606  * @cfg {Boolean} clickable (true|false) default false
1607
1608  *     
1609  * @constructor
1610  * Create a new Container
1611  * @param {Object} config The config object
1612  */
1613
1614 Roo.bootstrap.Container = function(config){
1615     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1616     
1617     this.addEvents({
1618         // raw events
1619          /**
1620          * @event expand
1621          * After the panel has been expand
1622          * 
1623          * @param {Roo.bootstrap.Container} this
1624          */
1625         "expand" : true,
1626         /**
1627          * @event collapse
1628          * After the panel has been collapsed
1629          * 
1630          * @param {Roo.bootstrap.Container} this
1631          */
1632         "collapse" : true,
1633         /**
1634          * @event click
1635          * When a element is chick
1636          * @param {Roo.bootstrap.Container} this
1637          * @param {Roo.EventObject} e
1638          */
1639         "click" : true
1640     });
1641 };
1642
1643 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1644     
1645     jumbotron : false,
1646     well: '',
1647     panel : '',
1648     header: '',
1649     footer : '',
1650     sticky: '',
1651     tag : false,
1652     alert : false,
1653     fa: false,
1654     icon : false,
1655     expandable : false,
1656     rheader : '',
1657     expanded : true,
1658     clickable: false,
1659   
1660      
1661     getChildContainer : function() {
1662         
1663         if(!this.el){
1664             return false;
1665         }
1666         
1667         if (this.panel.length) {
1668             return this.el.select('.panel-body',true).first();
1669         }
1670         
1671         return this.el;
1672     },
1673     
1674     
1675     getAutoCreate : function(){
1676         
1677         var cfg = {
1678             tag : this.tag || 'div',
1679             html : '',
1680             cls : ''
1681         };
1682         if (this.jumbotron) {
1683             cfg.cls = 'jumbotron';
1684         }
1685         
1686         
1687         
1688         // - this is applied by the parent..
1689         //if (this.cls) {
1690         //    cfg.cls = this.cls + '';
1691         //}
1692         
1693         if (this.sticky.length) {
1694             
1695             var bd = Roo.get(document.body);
1696             if (!bd.hasClass('bootstrap-sticky')) {
1697                 bd.addClass('bootstrap-sticky');
1698                 Roo.select('html',true).setStyle('height', '100%');
1699             }
1700              
1701             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1702         }
1703         
1704         
1705         if (this.well.length) {
1706             switch (this.well) {
1707                 case 'lg':
1708                 case 'sm':
1709                     cfg.cls +=' well well-' +this.well;
1710                     break;
1711                 default:
1712                     cfg.cls +=' well';
1713                     break;
1714             }
1715         }
1716         
1717         if (this.hidden) {
1718             cfg.cls += ' hidden';
1719         }
1720         
1721         
1722         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1723             cfg.cls +=' alert alert-' + this.alert;
1724         }
1725         
1726         var body = cfg;
1727         
1728         if (this.panel.length) {
1729             cfg.cls += ' panel panel-' + this.panel;
1730             cfg.cn = [];
1731             if (this.header.length) {
1732                 
1733                 var h = [];
1734                 
1735                 if(this.expandable){
1736                     
1737                     cfg.cls = cfg.cls + ' expandable';
1738                     
1739                     h.push({
1740                         tag: 'i',
1741                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1742                     });
1743                     
1744                 }
1745                 
1746                 h.push(
1747                     {
1748                         tag: 'span',
1749                         cls : 'panel-title',
1750                         html : (this.expandable ? '&nbsp;' : '') + this.header
1751                     },
1752                     {
1753                         tag: 'span',
1754                         cls: 'panel-header-right',
1755                         html: this.rheader
1756                     }
1757                 );
1758                 
1759                 cfg.cn.push({
1760                     cls : 'panel-heading',
1761                     style : this.expandable ? 'cursor: pointer' : '',
1762                     cn : h
1763                 });
1764                 
1765             }
1766             
1767             body = false;
1768             cfg.cn.push({
1769                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1770                 html : this.html
1771             });
1772             
1773             
1774             if (this.footer.length) {
1775                 cfg.cn.push({
1776                     cls : 'panel-footer',
1777                     html : this.footer
1778                     
1779                 });
1780             }
1781             
1782         }
1783         
1784         if (body) {
1785             body.html = this.html || cfg.html;
1786             // prefix with the icons..
1787             if (this.fa) {
1788                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1789             }
1790             if (this.icon) {
1791                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1792             }
1793             
1794             
1795         }
1796         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1797             cfg.cls =  'container';
1798         }
1799         
1800         return cfg;
1801     },
1802     
1803     initEvents: function() 
1804     {
1805         if(this.expandable){
1806             var headerEl = this.headerEl();
1807         
1808             if(headerEl){
1809                 headerEl.on('click', this.onToggleClick, this);
1810             }
1811         }
1812         
1813         if(this.clickable){
1814             this.el.on('click', this.onClick, this);
1815         }
1816         
1817     },
1818     
1819     onToggleClick : function()
1820     {
1821         var headerEl = this.headerEl();
1822         
1823         if(!headerEl){
1824             return;
1825         }
1826         
1827         if(this.expanded){
1828             this.collapse();
1829             return;
1830         }
1831         
1832         this.expand();
1833     },
1834     
1835     expand : function()
1836     {
1837         if(this.fireEvent('expand', this)) {
1838             
1839             this.expanded = true;
1840             
1841             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1842             
1843             this.el.select('.panel-body',true).first().removeClass('hide');
1844             
1845             var toggleEl = this.toggleEl();
1846
1847             if(!toggleEl){
1848                 return;
1849             }
1850
1851             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1852         }
1853         
1854     },
1855     
1856     collapse : function()
1857     {
1858         if(this.fireEvent('collapse', this)) {
1859             
1860             this.expanded = false;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1863             this.el.select('.panel-body',true).first().addClass('hide');
1864         
1865             var toggleEl = this.toggleEl();
1866
1867             if(!toggleEl){
1868                 return;
1869             }
1870
1871             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1872         }
1873     },
1874     
1875     toggleEl : function()
1876     {
1877         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1878             return;
1879         }
1880         
1881         return this.el.select('.panel-heading .fa',true).first();
1882     },
1883     
1884     headerEl : function()
1885     {
1886         if(!this.el || !this.panel.length || !this.header.length){
1887             return;
1888         }
1889         
1890         return this.el.select('.panel-heading',true).first()
1891     },
1892     
1893     bodyEl : function()
1894     {
1895         if(!this.el || !this.panel.length){
1896             return;
1897         }
1898         
1899         return this.el.select('.panel-body',true).first()
1900     },
1901     
1902     titleEl : function()
1903     {
1904         if(!this.el || !this.panel.length || !this.header.length){
1905             return;
1906         }
1907         
1908         return this.el.select('.panel-title',true).first();
1909     },
1910     
1911     setTitle : function(v)
1912     {
1913         var titleEl = this.titleEl();
1914         
1915         if(!titleEl){
1916             return;
1917         }
1918         
1919         titleEl.dom.innerHTML = v;
1920     },
1921     
1922     getTitle : function()
1923     {
1924         
1925         var titleEl = this.titleEl();
1926         
1927         if(!titleEl){
1928             return '';
1929         }
1930         
1931         return titleEl.dom.innerHTML;
1932     },
1933     
1934     setRightTitle : function(v)
1935     {
1936         var t = this.el.select('.panel-header-right',true).first();
1937         
1938         if(!t){
1939             return;
1940         }
1941         
1942         t.dom.innerHTML = v;
1943     },
1944     
1945     onClick : function(e)
1946     {
1947         e.preventDefault();
1948         
1949         this.fireEvent('click', this, e);
1950     }
1951 });
1952
1953  /*
1954  *  - LGPL
1955  *
1956  *  This is BS4's Card element.. - similar to our containers probably..
1957  * 
1958  */
1959 /**
1960  * @class Roo.bootstrap.Card
1961  * @extends Roo.bootstrap.Component
1962  * Bootstrap Card class
1963  *
1964  *
1965  * possible... may not be implemented..
1966  * @cfg {String} header_image  src url of image.
1967  * @cfg {String|Object} header
1968  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1969  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1970  * 
1971  * @cfg {String} title
1972  * @cfg {String} subtitle
1973  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1974  * @cfg {String} footer
1975  
1976  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1977  * 
1978  * @cfg {String} margin (0|1|2|3|4|5|auto)
1979  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1980  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1981  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1982  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1983  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1985  *
1986  * @cfg {String} padding (0|1|2|3|4|5)
1987  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1988  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1989  * @cfg {String} padding_left (0|1|2|3|4|5)
1990  * @cfg {String} padding_right (0|1|2|3|4|5)
1991  * @cfg {String} padding_x (0|1|2|3|4|5)
1992  * @cfg {String} padding_y (0|1|2|3|4|5)
1993  *
1994  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1995  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1996  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1997  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1998  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1999  
2000  * @config {Boolean} dragable  if this card can be dragged.
2001  * @config {String} drag_group  group for drag
2002  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2003  * @config {String} drop_group  group for drag
2004  * 
2005  * @config {Boolean} collapsable can the body be collapsed.
2006  * @config {Boolean} collapsed is the body collapsed when rendered...
2007  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2008  * @config {Boolean} rotated is the body rotated when rendered...
2009  * 
2010  * @constructor
2011  * Create a new Container
2012  * @param {Object} config The config object
2013  */
2014
2015 Roo.bootstrap.Card = function(config){
2016     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2017     
2018     this.addEvents({
2019          // raw events
2020         /**
2021          * @event drop
2022          * When a element a card is dropped
2023          * @param {Roo.bootstrap.Card} this
2024          *
2025          * 
2026          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2027          * @param {String} position 'above' or 'below'
2028          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2029         
2030          */
2031         'drop' : true,
2032          /**
2033          * @event rotate
2034          * When a element a card is rotate
2035          * @param {Roo.bootstrap.Card} this
2036          * @param {Roo.Element} n the node being dropped?
2037          * @param {Boolean} rotate status
2038          */
2039         'rotate' : true,
2040         /**
2041          * @event cardover
2042          * When a card element is dragged over ready to drop (return false to block dropable)
2043          * @param {Roo.bootstrap.Card} this
2044          * @param {Object} data from dragdrop 
2045          */
2046          'cardover' : true
2047          
2048     });
2049 };
2050
2051
2052 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2053     
2054     
2055     weight : '',
2056     
2057     margin: '', /// may be better in component?
2058     margin_top: '', 
2059     margin_bottom: '', 
2060     margin_left: '',
2061     margin_right: '',
2062     margin_x: '',
2063     margin_y: '',
2064     
2065     padding : '',
2066     padding_top: '', 
2067     padding_bottom: '', 
2068     padding_left: '',
2069     padding_right: '',
2070     padding_x: '',
2071     padding_y: '',
2072     
2073     display: '', 
2074     display_xs: '', 
2075     display_sm: '', 
2076     display_lg: '',
2077     display_xl: '',
2078  
2079     header_image  : '',
2080     header : '',
2081     header_size : 0,
2082     title : '',
2083     subtitle : '',
2084     html : '',
2085     footer: '',
2086
2087     collapsable : false,
2088     collapsed : false,
2089     rotateable : false,
2090     rotated : false,
2091     
2092     dragable : false,
2093     drag_group : false,
2094     dropable : false,
2095     drop_group : false,
2096     childContainer : false,
2097     dropEl : false, /// the dom placeholde element that indicates drop location.
2098     containerEl: false, // body container
2099     bodyEl: false, // card-body
2100     headerContainerEl : false, //
2101     headerEl : false,
2102     header_imageEl : false,
2103     
2104     
2105     layoutCls : function()
2106     {
2107         var cls = '';
2108         var t = this;
2109         Roo.log(this.margin_bottom.length);
2110         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2111             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2112             
2113             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2114                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2115             }
2116             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2117                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2118             }
2119         });
2120         
2121         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2122             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2124             }
2125         });
2126         
2127         // more generic support?
2128         if (this.hidden) {
2129             cls += ' d-none';
2130         }
2131         
2132         return cls;
2133     },
2134  
2135        // Roo.log("Call onRender: " + this.xtype);
2136         /*  We are looking at something like this.
2137 <div class="card">
2138     <img src="..." class="card-img-top" alt="...">
2139     <div class="card-body">
2140         <h5 class="card-title">Card title</h5>
2141          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2142
2143         >> this bit is really the body...
2144         <div> << we will ad dthis in hopefully it will not break shit.
2145         
2146         ** card text does not actually have any styling...
2147         
2148             <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>
2149         
2150         </div> <<
2151           <a href="#" class="card-link">Card link</a>
2152           
2153     </div>
2154     <div class="card-footer">
2155         <small class="text-muted">Last updated 3 mins ago</small>
2156     </div>
2157 </div>
2158          */
2159     getAutoCreate : function(){
2160         
2161         var cfg = {
2162             tag : 'div',
2163             cls : 'card',
2164             cn : [ ]
2165         };
2166         
2167         if (this.weight.length && this.weight != 'light') {
2168             cfg.cls += ' text-white';
2169         } else {
2170             cfg.cls += ' text-dark'; // need as it's nested..
2171         }
2172         if (this.weight.length) {
2173             cfg.cls += ' bg-' + this.weight;
2174         }
2175         
2176         cfg.cls += ' ' + this.layoutCls(); 
2177         
2178         var hdr = false;
2179         var hdr_ctr = false;
2180         if (this.header.length) {
2181             hdr = {
2182                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2183                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2184                 cn : []
2185             };
2186             cfg.cn.push(hdr);
2187             hdr_ctr = hdr;
2188         } else {
2189             hdr = {
2190                 tag : 'div',
2191                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2192                 cn : []
2193             };
2194             cfg.cn.push(hdr);
2195             hdr_ctr = hdr;
2196         }
2197         if (this.collapsable) {
2198             hdr_ctr = {
2199             tag : 'a',
2200             cls : 'd-block user-select-none',
2201             cn: [
2202                     {
2203                         tag: 'i',
2204                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2205                     }
2206                    
2207                 ]
2208             };
2209             hdr.cn.push(hdr_ctr);
2210         }
2211         
2212         hdr_ctr.cn.push(        {
2213             tag: 'span',
2214             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2215             html : this.header
2216         });
2217         
2218         
2219         if (this.header_image.length) {
2220             cfg.cn.push({
2221                 tag : 'img',
2222                 cls : 'card-img-top',
2223                 src: this.header_image // escape?
2224             });
2225         } else {
2226             cfg.cn.push({
2227                     tag : 'div',
2228                     cls : 'card-img-top d-none' 
2229                 });
2230         }
2231             
2232         var body = {
2233             tag : 'div',
2234             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2235             cn : []
2236         };
2237         var obody = body;
2238         if (this.collapsable || this.rotateable) {
2239             obody = {
2240                 tag: 'div',
2241                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2242                 cn : [  body ]
2243             };
2244         }
2245         
2246         cfg.cn.push(obody);
2247         
2248         if (this.title.length) {
2249             body.cn.push({
2250                 tag : 'div',
2251                 cls : 'card-title',
2252                 src: this.title // escape?
2253             });
2254         }  
2255         
2256         if (this.subtitle.length) {
2257             body.cn.push({
2258                 tag : 'div',
2259                 cls : 'card-title',
2260                 src: this.subtitle // escape?
2261             });
2262         }
2263         
2264         body.cn.push({
2265             tag : 'div',
2266             cls : 'roo-card-body-ctr'
2267         });
2268         
2269         if (this.html.length) {
2270             body.cn.push({
2271                 tag: 'div',
2272                 html : this.html
2273             });
2274         }
2275         // fixme ? handle objects?
2276         
2277         if (this.footer.length) {
2278            
2279             cfg.cn.push({
2280                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2281                 html : this.footer
2282             });
2283             
2284         } else {
2285             cfg.cn.push({cls : 'card-footer d-none'});
2286         }
2287         
2288         // footer...
2289         
2290         return cfg;
2291     },
2292     
2293     
2294     getCardHeader : function()
2295     {
2296         var  ret = this.el.select('.card-header',true).first();
2297         if (ret.hasClass('d-none')) {
2298             ret.removeClass('d-none');
2299         }
2300         
2301         return ret;
2302     },
2303     getCardFooter : function()
2304     {
2305         var  ret = this.el.select('.card-footer',true).first();
2306         if (ret.hasClass('d-none')) {
2307             ret.removeClass('d-none');
2308         }
2309         
2310         return ret;
2311     },
2312     getCardImageTop : function()
2313     {
2314         var  ret = this.header_imageEl;
2315         if (ret.hasClass('d-none')) {
2316             ret.removeClass('d-none');
2317         }
2318             
2319         return ret;
2320     },
2321     
2322     getChildContainer : function()
2323     {
2324         
2325         if(!this.el){
2326             return false;
2327         }
2328         return this.el.select('.roo-card-body-ctr',true).first();    
2329     },
2330     
2331     initEvents: function() 
2332     {
2333         this.bodyEl = this.el.select('.card-body',true).first(); 
2334         this.containerEl = this.getChildContainer();
2335         if(this.dragable){
2336             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2337                     containerScroll: true,
2338                     ddGroup: this.drag_group || 'default_card_drag_group'
2339             });
2340             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2341         }
2342         if (this.dropable) {
2343             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2344                 containerScroll: true,
2345                 ddGroup: this.drop_group || 'default_card_drag_group'
2346             });
2347             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2348             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2349             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2350             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2351             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2352         }
2353         
2354         if (this.collapsable) {
2355             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2356         }
2357         if (this.rotateable) {
2358             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2359         }
2360         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2361          
2362         this.footerEl = this.el.select('.card-footer',true).first();
2363         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2364         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2365         this.headerEl = this.el.select('.card-header',true).first();
2366         
2367         if (this.rotated) {
2368             this.el.addClass('roo-card-rotated');
2369             this.fireEvent('rotate', this, true);
2370         }
2371         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2372         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2373         
2374     },
2375     getDragData : function(e)
2376     {
2377         var target = this.getEl();
2378         if (target) {
2379             //this.handleSelection(e);
2380             
2381             var dragData = {
2382                 source: this,
2383                 copy: false,
2384                 nodes: this.getEl(),
2385                 records: []
2386             };
2387             
2388             
2389             dragData.ddel = target.dom ;    // the div element
2390             Roo.log(target.getWidth( ));
2391             dragData.ddel.style.width = target.getWidth() + 'px';
2392             
2393             return dragData;
2394         }
2395         return false;
2396     },
2397     /**
2398     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2399     *    whole Element becomes the target, and this causes the drop gesture to append.
2400     *
2401     *    Returns an object:
2402     *     {
2403            
2404            position : 'below' or 'above'
2405            card  : relateive to card OBJECT (or true for no cards listed)
2406            items_n : relative to nth item in list
2407            card_n : relative to  nth card in list
2408     }
2409     *
2410     *    
2411     */
2412     getTargetFromEvent : function(e, dragged_card_el)
2413     {
2414         var target = e.getTarget();
2415         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2416             target = target.parentNode;
2417         }
2418         
2419         var ret = {
2420             position: '',
2421             cards : [],
2422             card_n : -1,
2423             items_n : -1,
2424             card : false 
2425         };
2426         
2427         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2428         // see if target is one of the 'cards'...
2429         
2430         
2431         //Roo.log(this.items.length);
2432         var pos = false;
2433         
2434         var last_card_n = 0;
2435         var cards_len  = 0;
2436         for (var i = 0;i< this.items.length;i++) {
2437             
2438             if (!this.items[i].el.hasClass('card')) {
2439                  continue;
2440             }
2441             pos = this.getDropPoint(e, this.items[i].el.dom);
2442             
2443             cards_len = ret.cards.length;
2444             //Roo.log(this.items[i].el.dom.id);
2445             ret.cards.push(this.items[i]);
2446             last_card_n  = i;
2447             if (ret.card_n < 0 && pos == 'above') {
2448                 ret.position = cards_len > 0 ? 'below' : pos;
2449                 ret.items_n = i > 0 ? i - 1 : 0;
2450                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2451                 ret.card = ret.cards[ret.card_n];
2452             }
2453         }
2454         if (!ret.cards.length) {
2455             ret.card = true;
2456             ret.position = 'below';
2457             ret.items_n;
2458             return ret;
2459         }
2460         // could not find a card.. stick it at the end..
2461         if (ret.card_n < 0) {
2462             ret.card_n = last_card_n;
2463             ret.card = ret.cards[last_card_n];
2464             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2465             ret.position = 'below';
2466         }
2467         
2468         if (this.items[ret.items_n].el == dragged_card_el) {
2469             return false;
2470         }
2471         
2472         if (ret.position == 'below') {
2473             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2474             
2475             if (card_after  && card_after.el == dragged_card_el) {
2476                 return false;
2477             }
2478             return ret;
2479         }
2480         
2481         // its's after ..
2482         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2483         
2484         if (card_before  && card_before.el == dragged_card_el) {
2485             return false;
2486         }
2487         
2488         return ret;
2489     },
2490     
2491     onNodeEnter : function(n, dd, e, data){
2492         return false;
2493     },
2494     onNodeOver : function(n, dd, e, data)
2495     {
2496        
2497         var target_info = this.getTargetFromEvent(e,data.source.el);
2498         if (target_info === false) {
2499             this.dropPlaceHolder('hide');
2500             return false;
2501         }
2502         Roo.log(['getTargetFromEvent', target_info ]);
2503         
2504         
2505         if (this.fireEvent('cardover', this, [ data ]) === false) {
2506             return false;
2507         }
2508         
2509         this.dropPlaceHolder('show', target_info,data);
2510         
2511         return false; 
2512     },
2513     onNodeOut : function(n, dd, e, data){
2514         this.dropPlaceHolder('hide');
2515      
2516     },
2517     onNodeDrop : function(n, dd, e, data)
2518     {
2519         
2520         // call drop - return false if
2521         
2522         // this could actually fail - if the Network drops..
2523         // we will ignore this at present..- client should probably reload
2524         // the whole set of cards if stuff like that fails.
2525         
2526         
2527         var info = this.getTargetFromEvent(e,data.source.el);
2528         if (info === false) {
2529             return false;
2530         }
2531         this.dropPlaceHolder('hide');
2532   
2533           
2534     
2535         this.acceptCard(data.source, info.position, info.card, info.items_n);
2536         return true;
2537          
2538     },
2539     firstChildCard : function()
2540     {
2541         for (var i = 0;i< this.items.length;i++) {
2542             
2543             if (!this.items[i].el.hasClass('card')) {
2544                  continue;
2545             }
2546             return this.items[i];
2547         }
2548         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2549     },
2550     /**
2551      * accept card
2552      *
2553      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2554      */
2555     acceptCard : function(move_card,  position, next_to_card )
2556     {
2557         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2558             return false;
2559         }
2560         
2561         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2562         
2563         move_card.parent().removeCard(move_card);
2564         
2565         
2566         var dom = move_card.el.dom;
2567         dom.style.width = ''; // clear with - which is set by drag.
2568         
2569         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2570             var cardel = next_to_card.el.dom;
2571             
2572             if (position == 'above' ) {
2573                 cardel.parentNode.insertBefore(dom, cardel);
2574             } else if (cardel.nextSibling) {
2575                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2576             } else {
2577                 cardel.parentNode.append(dom);
2578             }
2579         } else {
2580             // card container???
2581             this.containerEl.dom.append(dom);
2582         }
2583         
2584         //FIXME HANDLE card = true 
2585         
2586         // add this to the correct place in items.
2587         
2588         // remove Card from items.
2589         
2590        
2591         if (this.items.length) {
2592             var nitems = [];
2593             //Roo.log([info.items_n, info.position, this.items.length]);
2594             for (var i =0; i < this.items.length; i++) {
2595                 if (i == to_items_n && position == 'above') {
2596                     nitems.push(move_card);
2597                 }
2598                 nitems.push(this.items[i]);
2599                 if (i == to_items_n && position == 'below') {
2600                     nitems.push(move_card);
2601                 }
2602             }
2603             this.items = nitems;
2604             Roo.log(this.items);
2605         } else {
2606             this.items.push(move_card);
2607         }
2608         
2609         move_card.parentId = this.id;
2610         
2611         return true;
2612         
2613         
2614     },
2615     removeCard : function(c)
2616     {
2617         this.items = this.items.filter(function(e) { return e != c });
2618  
2619         var dom = c.el.dom;
2620         dom.parentNode.removeChild(dom);
2621         dom.style.width = ''; // clear with - which is set by drag.
2622         c.parentId = false;
2623         
2624     },
2625     
2626     /**    Decide whether to drop above or below a View node. */
2627     getDropPoint : function(e, n, dd)
2628     {
2629         if (dd) {
2630              return false;
2631         }
2632         if (n == this.containerEl.dom) {
2633             return "above";
2634         }
2635         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2636         var c = t + (b - t) / 2;
2637         var y = Roo.lib.Event.getPageY(e);
2638         if(y <= c) {
2639             return "above";
2640         }else{
2641             return "below";
2642         }
2643     },
2644     onToggleCollapse : function(e)
2645         {
2646         if (this.collapsed) {
2647             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2648             this.collapsableEl.addClass('show');
2649             this.collapsed = false;
2650             return;
2651         }
2652         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2653         this.collapsableEl.removeClass('show');
2654         this.collapsed = true;
2655         
2656     
2657     },
2658     
2659     onToggleRotate : function(e)
2660     {
2661         this.collapsableEl.removeClass('show');
2662         this.footerEl.removeClass('d-none');
2663         this.el.removeClass('roo-card-rotated');
2664         this.el.removeClass('d-none');
2665         if (this.rotated) {
2666             
2667             this.collapsableEl.addClass('show');
2668             this.rotated = false;
2669             this.fireEvent('rotate', this, this.rotated);
2670             return;
2671         }
2672         this.el.addClass('roo-card-rotated');
2673         this.footerEl.addClass('d-none');
2674         this.el.select('.roo-collapsable').removeClass('show');
2675         
2676         this.rotated = true;
2677         this.fireEvent('rotate', this, this.rotated);
2678     
2679     },
2680     
2681     dropPlaceHolder: function (action, info, data)
2682     {
2683         if (this.dropEl === false) {
2684             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2685             cls : 'd-none'
2686             },true);
2687         }
2688         this.dropEl.removeClass(['d-none', 'd-block']);        
2689         if (action == 'hide') {
2690             
2691             this.dropEl.addClass('d-none');
2692             return;
2693         }
2694         // FIXME - info.card == true!!!
2695         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2696         
2697         if (info.card !== true) {
2698             var cardel = info.card.el.dom;
2699             
2700             if (info.position == 'above') {
2701                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2702             } else if (cardel.nextSibling) {
2703                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2704             } else {
2705                 cardel.parentNode.append(this.dropEl.dom);
2706             }
2707         } else {
2708             // card container???
2709             this.containerEl.dom.append(this.dropEl.dom);
2710         }
2711         
2712         this.dropEl.addClass('d-block roo-card-dropzone');
2713         
2714         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2715         
2716         
2717     
2718     
2719     
2720     },
2721     setHeaderText: function(html)
2722     {
2723         this.header = html;
2724         if (this.headerContainerEl) {
2725             this.headerContainerEl.dom.innerHTML = html;
2726         }
2727     },
2728     onHeaderImageLoad : function(ev, he)
2729     {
2730         if (!this.header_image_fit_square) {
2731             return;
2732         }
2733         
2734         var hw = he.naturalHeight / he.naturalWidth;
2735         // wide image = < 0
2736         // tall image = > 1
2737         //var w = he.dom.naturalWidth;
2738         var ww = he.width;
2739         he.style.left =  0;
2740         he.style.position =  'relative';
2741         if (hw > 1) {
2742             var nw = (ww * (1/hw));
2743             Roo.get(he).setSize( ww * (1/hw),  ww);
2744             he.style.left =  ((ww - nw)/ 2) + 'px';
2745             he.style.position =  'relative';
2746         }
2747
2748     }
2749
2750     
2751 });
2752
2753 /*
2754  * - LGPL
2755  *
2756  * Card header - holder for the card header elements.
2757  * 
2758  */
2759
2760 /**
2761  * @class Roo.bootstrap.CardHeader
2762  * @extends Roo.bootstrap.Element
2763  * Bootstrap CardHeader class
2764  * @constructor
2765  * Create a new Card Header - that you can embed children into
2766  * @param {Object} config The config object
2767  */
2768
2769 Roo.bootstrap.CardHeader = function(config){
2770     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2771 };
2772
2773 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2774     
2775     
2776     container_method : 'getCardHeader' 
2777     
2778      
2779     
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * Card footer - holder for the card footer elements.
2790  * 
2791  */
2792
2793 /**
2794  * @class Roo.bootstrap.CardFooter
2795  * @extends Roo.bootstrap.Element
2796  * Bootstrap CardFooter class
2797  * @constructor
2798  * Create a new Card Footer - that you can embed children into
2799  * @param {Object} config The config object
2800  */
2801
2802 Roo.bootstrap.CardFooter = function(config){
2803     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2804 };
2805
2806 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2807     
2808     
2809     container_method : 'getCardFooter' 
2810     
2811      
2812     
2813     
2814    
2815 });
2816
2817  
2818
2819  /*
2820  * - LGPL
2821  *
2822  * Card header - holder for the card header elements.
2823  * 
2824  */
2825
2826 /**
2827  * @class Roo.bootstrap.CardImageTop
2828  * @extends Roo.bootstrap.Element
2829  * Bootstrap CardImageTop class
2830  * @constructor
2831  * Create a new Card Image Top container
2832  * @param {Object} config The config object
2833  */
2834
2835 Roo.bootstrap.CardImageTop = function(config){
2836     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2837 };
2838
2839 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2840     
2841    
2842     container_method : 'getCardImageTop' 
2843     
2844      
2845     
2846    
2847 });
2848
2849  
2850
2851  /*
2852  * - LGPL
2853  *
2854  * image
2855  * 
2856  */
2857
2858
2859 /**
2860  * @class Roo.bootstrap.Img
2861  * @extends Roo.bootstrap.Component
2862  * Bootstrap Img class
2863  * @cfg {Boolean} imgResponsive false | true
2864  * @cfg {String} border rounded | circle | thumbnail
2865  * @cfg {String} src image source
2866  * @cfg {String} alt image alternative text
2867  * @cfg {String} href a tag href
2868  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2869  * @cfg {String} xsUrl xs image source
2870  * @cfg {String} smUrl sm image source
2871  * @cfg {String} mdUrl md image source
2872  * @cfg {String} lgUrl lg image source
2873  * 
2874  * @constructor
2875  * Create a new Input
2876  * @param {Object} config The config object
2877  */
2878
2879 Roo.bootstrap.Img = function(config){
2880     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2881     
2882     this.addEvents({
2883         // img events
2884         /**
2885          * @event click
2886          * The img click event for the img.
2887          * @param {Roo.EventObject} e
2888          */
2889         "click" : true
2890     });
2891 };
2892
2893 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2894     
2895     imgResponsive: true,
2896     border: '',
2897     src: 'about:blank',
2898     href: false,
2899     target: false,
2900     xsUrl: '',
2901     smUrl: '',
2902     mdUrl: '',
2903     lgUrl: '',
2904
2905     getAutoCreate : function()
2906     {   
2907         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2908             return this.createSingleImg();
2909         }
2910         
2911         var cfg = {
2912             tag: 'div',
2913             cls: 'roo-image-responsive-group',
2914             cn: []
2915         };
2916         var _this = this;
2917         
2918         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2919             
2920             if(!_this[size + 'Url']){
2921                 return;
2922             }
2923             
2924             var img = {
2925                 tag: 'img',
2926                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2927                 html: _this.html || cfg.html,
2928                 src: _this[size + 'Url']
2929             };
2930             
2931             img.cls += ' roo-image-responsive-' + size;
2932             
2933             var s = ['xs', 'sm', 'md', 'lg'];
2934             
2935             s.splice(s.indexOf(size), 1);
2936             
2937             Roo.each(s, function(ss){
2938                 img.cls += ' hidden-' + ss;
2939             });
2940             
2941             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2942                 cfg.cls += ' img-' + _this.border;
2943             }
2944             
2945             if(_this.alt){
2946                 cfg.alt = _this.alt;
2947             }
2948             
2949             if(_this.href){
2950                 var a = {
2951                     tag: 'a',
2952                     href: _this.href,
2953                     cn: [
2954                         img
2955                     ]
2956                 };
2957
2958                 if(this.target){
2959                     a.target = _this.target;
2960                 }
2961             }
2962             
2963             cfg.cn.push((_this.href) ? a : img);
2964             
2965         });
2966         
2967         return cfg;
2968     },
2969     
2970     createSingleImg : function()
2971     {
2972         var cfg = {
2973             tag: 'img',
2974             cls: (this.imgResponsive) ? 'img-responsive' : '',
2975             html : null,
2976             src : 'about:blank'  // just incase src get's set to undefined?!?
2977         };
2978         
2979         cfg.html = this.html || cfg.html;
2980         
2981         cfg.src = this.src || cfg.src;
2982         
2983         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2984             cfg.cls += ' img-' + this.border;
2985         }
2986         
2987         if(this.alt){
2988             cfg.alt = this.alt;
2989         }
2990         
2991         if(this.href){
2992             var a = {
2993                 tag: 'a',
2994                 href: this.href,
2995                 cn: [
2996                     cfg
2997                 ]
2998             };
2999             
3000             if(this.target){
3001                 a.target = this.target;
3002             }
3003             
3004         }
3005         
3006         return (this.href) ? a : cfg;
3007     },
3008     
3009     initEvents: function() 
3010     {
3011         if(!this.href){
3012             this.el.on('click', this.onClick, this);
3013         }
3014         
3015     },
3016     
3017     onClick : function(e)
3018     {
3019         Roo.log('img onclick');
3020         this.fireEvent('click', this, e);
3021     },
3022     /**
3023      * Sets the url of the image - used to update it
3024      * @param {String} url the url of the image
3025      */
3026     
3027     setSrc : function(url)
3028     {
3029         this.src =  url;
3030         
3031         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3032             this.el.dom.src =  url;
3033             return;
3034         }
3035         
3036         this.el.select('img', true).first().dom.src =  url;
3037     }
3038     
3039     
3040    
3041 });
3042
3043  /*
3044  * - LGPL
3045  *
3046  * image
3047  * 
3048  */
3049
3050
3051 /**
3052  * @class Roo.bootstrap.Link
3053  * @extends Roo.bootstrap.Component
3054  * Bootstrap Link Class
3055  * @cfg {String} alt image alternative text
3056  * @cfg {String} href a tag href
3057  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3058  * @cfg {String} html the content of the link.
3059  * @cfg {String} anchor name for the anchor link
3060  * @cfg {String} fa - favicon
3061
3062  * @cfg {Boolean} preventDefault (true | false) default false
3063
3064  * 
3065  * @constructor
3066  * Create a new Input
3067  * @param {Object} config The config object
3068  */
3069
3070 Roo.bootstrap.Link = function(config){
3071     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3072     
3073     this.addEvents({
3074         // img events
3075         /**
3076          * @event click
3077          * The img click event for the img.
3078          * @param {Roo.EventObject} e
3079          */
3080         "click" : true
3081     });
3082 };
3083
3084 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3085     
3086     href: false,
3087     target: false,
3088     preventDefault: false,
3089     anchor : false,
3090     alt : false,
3091     fa: false,
3092
3093
3094     getAutoCreate : function()
3095     {
3096         var html = this.html || '';
3097         
3098         if (this.fa !== false) {
3099             html = '<i class="fa fa-' + this.fa + '"></i>';
3100         }
3101         var cfg = {
3102             tag: 'a'
3103         };
3104         // anchor's do not require html/href...
3105         if (this.anchor === false) {
3106             cfg.html = html;
3107             cfg.href = this.href || '#';
3108         } else {
3109             cfg.name = this.anchor;
3110             if (this.html !== false || this.fa !== false) {
3111                 cfg.html = html;
3112             }
3113             if (this.href !== false) {
3114                 cfg.href = this.href;
3115             }
3116         }
3117         
3118         if(this.alt !== false){
3119             cfg.alt = this.alt;
3120         }
3121         
3122         
3123         if(this.target !== false) {
3124             cfg.target = this.target;
3125         }
3126         
3127         return cfg;
3128     },
3129     
3130     initEvents: function() {
3131         
3132         if(!this.href || this.preventDefault){
3133             this.el.on('click', this.onClick, this);
3134         }
3135     },
3136     
3137     onClick : function(e)
3138     {
3139         if(this.preventDefault){
3140             e.preventDefault();
3141         }
3142         //Roo.log('img onclick');
3143         this.fireEvent('click', this, e);
3144     }
3145    
3146 });
3147
3148  /*
3149  * - LGPL
3150  *
3151  * header
3152  * 
3153  */
3154
3155 /**
3156  * @class Roo.bootstrap.Header
3157  * @extends Roo.bootstrap.Component
3158  * Bootstrap Header class
3159  * @cfg {String} html content of header
3160  * @cfg {Number} level (1|2|3|4|5|6) default 1
3161  * 
3162  * @constructor
3163  * Create a new Header
3164  * @param {Object} config The config object
3165  */
3166
3167
3168 Roo.bootstrap.Header  = function(config){
3169     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3170 };
3171
3172 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3173     
3174     //href : false,
3175     html : false,
3176     level : 1,
3177     
3178     
3179     
3180     getAutoCreate : function(){
3181         
3182         
3183         
3184         var cfg = {
3185             tag: 'h' + (1 *this.level),
3186             html: this.html || ''
3187         } ;
3188         
3189         return cfg;
3190     }
3191    
3192 });
3193
3194  
3195
3196  /*
3197  * Based on:
3198  * Ext JS Library 1.1.1
3199  * Copyright(c) 2006-2007, Ext JS, LLC.
3200  *
3201  * Originally Released Under LGPL - original licence link has changed is not relivant.
3202  *
3203  * Fork - LGPL
3204  * <script type="text/javascript">
3205  */
3206  
3207 /**
3208  * @class Roo.bootstrap.MenuMgr
3209  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3210  * @singleton
3211  */
3212 Roo.bootstrap.MenuMgr = function(){
3213    var menus, active, groups = {}, attached = false, lastShow = new Date();
3214
3215    // private - called when first menu is created
3216    function init(){
3217        menus = {};
3218        active = new Roo.util.MixedCollection();
3219        Roo.get(document).addKeyListener(27, function(){
3220            if(active.length > 0){
3221                hideAll();
3222            }
3223        });
3224    }
3225
3226    // private
3227    function hideAll(){
3228        if(active && active.length > 0){
3229            var c = active.clone();
3230            c.each(function(m){
3231                m.hide();
3232            });
3233        }
3234    }
3235
3236    // private
3237    function onHide(m){
3238        active.remove(m);
3239        if(active.length < 1){
3240            Roo.get(document).un("mouseup", onMouseDown);
3241             
3242            attached = false;
3243        }
3244    }
3245
3246    // private
3247    function onShow(m){
3248        var last = active.last();
3249        lastShow = new Date();
3250        active.add(m);
3251        if(!attached){
3252           Roo.get(document).on("mouseup", onMouseDown);
3253            
3254            attached = true;
3255        }
3256        if(m.parentMenu){
3257           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3258           m.parentMenu.activeChild = m;
3259        }else if(last && last.isVisible()){
3260           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3261        }
3262    }
3263
3264    // private
3265    function onBeforeHide(m){
3266        if(m.activeChild){
3267            m.activeChild.hide();
3268        }
3269        if(m.autoHideTimer){
3270            clearTimeout(m.autoHideTimer);
3271            delete m.autoHideTimer;
3272        }
3273    }
3274
3275    // private
3276    function onBeforeShow(m){
3277        var pm = m.parentMenu;
3278        if(!pm && !m.allowOtherMenus){
3279            hideAll();
3280        }else if(pm && pm.activeChild && active != m){
3281            pm.activeChild.hide();
3282        }
3283    }
3284
3285    // private this should really trigger on mouseup..
3286    function onMouseDown(e){
3287         Roo.log("on Mouse Up");
3288         
3289         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3290             Roo.log("MenuManager hideAll");
3291             hideAll();
3292             e.stopEvent();
3293         }
3294         
3295         
3296    }
3297
3298    // private
3299    function onBeforeCheck(mi, state){
3300        if(state){
3301            var g = groups[mi.group];
3302            for(var i = 0, l = g.length; i < l; i++){
3303                if(g[i] != mi){
3304                    g[i].setChecked(false);
3305                }
3306            }
3307        }
3308    }
3309
3310    return {
3311
3312        /**
3313         * Hides all menus that are currently visible
3314         */
3315        hideAll : function(){
3316             hideAll();  
3317        },
3318
3319        // private
3320        register : function(menu){
3321            if(!menus){
3322                init();
3323            }
3324            menus[menu.id] = menu;
3325            menu.on("beforehide", onBeforeHide);
3326            menu.on("hide", onHide);
3327            menu.on("beforeshow", onBeforeShow);
3328            menu.on("show", onShow);
3329            var g = menu.group;
3330            if(g && menu.events["checkchange"]){
3331                if(!groups[g]){
3332                    groups[g] = [];
3333                }
3334                groups[g].push(menu);
3335                menu.on("checkchange", onCheck);
3336            }
3337        },
3338
3339         /**
3340          * Returns a {@link Roo.menu.Menu} object
3341          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3342          * be used to generate and return a new Menu instance.
3343          */
3344        get : function(menu){
3345            if(typeof menu == "string"){ // menu id
3346                return menus[menu];
3347            }else if(menu.events){  // menu instance
3348                return menu;
3349            }
3350            /*else if(typeof menu.length == 'number'){ // array of menu items?
3351                return new Roo.bootstrap.Menu({items:menu});
3352            }else{ // otherwise, must be a config
3353                return new Roo.bootstrap.Menu(menu);
3354            }
3355            */
3356            return false;
3357        },
3358
3359        // private
3360        unregister : function(menu){
3361            delete menus[menu.id];
3362            menu.un("beforehide", onBeforeHide);
3363            menu.un("hide", onHide);
3364            menu.un("beforeshow", onBeforeShow);
3365            menu.un("show", onShow);
3366            var g = menu.group;
3367            if(g && menu.events["checkchange"]){
3368                groups[g].remove(menu);
3369                menu.un("checkchange", onCheck);
3370            }
3371        },
3372
3373        // private
3374        registerCheckable : function(menuItem){
3375            var g = menuItem.group;
3376            if(g){
3377                if(!groups[g]){
3378                    groups[g] = [];
3379                }
3380                groups[g].push(menuItem);
3381                menuItem.on("beforecheckchange", onBeforeCheck);
3382            }
3383        },
3384
3385        // private
3386        unregisterCheckable : function(menuItem){
3387            var g = menuItem.group;
3388            if(g){
3389                groups[g].remove(menuItem);
3390                menuItem.un("beforecheckchange", onBeforeCheck);
3391            }
3392        }
3393    };
3394 }();/*
3395  * - LGPL
3396  *
3397  * menu
3398  * 
3399  */
3400
3401 /**
3402  * @class Roo.bootstrap.Menu
3403  * @extends Roo.bootstrap.Component
3404  * Bootstrap Menu class - container for MenuItems
3405  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3406  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3407  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3408  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3409  * 
3410  * @constructor
3411  * Create a new Menu
3412  * @param {Object} config The config object
3413  */
3414
3415
3416 Roo.bootstrap.Menu = function(config){
3417     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3418     if (this.registerMenu && this.type != 'treeview')  {
3419         Roo.bootstrap.MenuMgr.register(this);
3420     }
3421     
3422     
3423     this.addEvents({
3424         /**
3425          * @event beforeshow
3426          * Fires before this menu is displayed (return false to block)
3427          * @param {Roo.menu.Menu} this
3428          */
3429         beforeshow : true,
3430         /**
3431          * @event beforehide
3432          * Fires before this menu is hidden (return false to block)
3433          * @param {Roo.menu.Menu} this
3434          */
3435         beforehide : true,
3436         /**
3437          * @event show
3438          * Fires after this menu is displayed
3439          * @param {Roo.menu.Menu} this
3440          */
3441         show : true,
3442         /**
3443          * @event hide
3444          * Fires after this menu is hidden
3445          * @param {Roo.menu.Menu} this
3446          */
3447         hide : true,
3448         /**
3449          * @event click
3450          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3451          * @param {Roo.menu.Menu} this
3452          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3453          * @param {Roo.EventObject} e
3454          */
3455         click : true,
3456         /**
3457          * @event mouseover
3458          * Fires when the mouse is hovering over this menu
3459          * @param {Roo.menu.Menu} this
3460          * @param {Roo.EventObject} e
3461          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3462          */
3463         mouseover : true,
3464         /**
3465          * @event mouseout
3466          * Fires when the mouse exits this menu
3467          * @param {Roo.menu.Menu} this
3468          * @param {Roo.EventObject} e
3469          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3470          */
3471         mouseout : true,
3472         /**
3473          * @event itemclick
3474          * Fires when a menu item contained in this menu is clicked
3475          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3476          * @param {Roo.EventObject} e
3477          */
3478         itemclick: true
3479     });
3480     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3481 };
3482
3483 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3484     
3485    /// html : false,
3486     //align : '',
3487     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3488     type: false,
3489     /**
3490      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3491      */
3492     registerMenu : true,
3493     
3494     menuItems :false, // stores the menu items..
3495     
3496     hidden:true,
3497         
3498     parentMenu : false,
3499     
3500     stopEvent : true,
3501     
3502     isLink : false,
3503     
3504     getChildContainer : function() {
3505         return this.el;  
3506     },
3507     
3508     getAutoCreate : function(){
3509          
3510         //if (['right'].indexOf(this.align)!==-1) {
3511         //    cfg.cn[1].cls += ' pull-right'
3512         //}
3513         
3514         
3515         var cfg = {
3516             tag : 'ul',
3517             cls : 'dropdown-menu' ,
3518             style : 'z-index:1000'
3519             
3520         };
3521         
3522         if (this.type === 'submenu') {
3523             cfg.cls = 'submenu active';
3524         }
3525         if (this.type === 'treeview') {
3526             cfg.cls = 'treeview-menu';
3527         }
3528         
3529         return cfg;
3530     },
3531     initEvents : function() {
3532         
3533        // Roo.log("ADD event");
3534        // Roo.log(this.triggerEl.dom);
3535         
3536         this.triggerEl.on('click', this.onTriggerClick, this);
3537         
3538         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3539         
3540         
3541         if (this.triggerEl.hasClass('nav-item')) {
3542             // dropdown toggle on the 'a' in BS4?
3543             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3544         } else {
3545             this.triggerEl.addClass('dropdown-toggle');
3546         }
3547         if (Roo.isTouch) {
3548             this.el.on('touchstart'  , this.onTouch, this);
3549         }
3550         this.el.on('click' , this.onClick, this);
3551
3552         this.el.on("mouseover", this.onMouseOver, this);
3553         this.el.on("mouseout", this.onMouseOut, this);
3554         
3555     },
3556     
3557     findTargetItem : function(e)
3558     {
3559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3560         if(!t){
3561             return false;
3562         }
3563         //Roo.log(t);         Roo.log(t.id);
3564         if(t && t.id){
3565             //Roo.log(this.menuitems);
3566             return this.menuitems.get(t.id);
3567             
3568             //return this.items.get(t.menuItemId);
3569         }
3570         
3571         return false;
3572     },
3573     
3574     onTouch : function(e) 
3575     {
3576         Roo.log("menu.onTouch");
3577         //e.stopEvent(); this make the user popdown broken
3578         this.onClick(e);
3579     },
3580     
3581     onClick : function(e)
3582     {
3583         Roo.log("menu.onClick");
3584         
3585         var t = this.findTargetItem(e);
3586         if(!t || t.isContainer){
3587             return;
3588         }
3589         Roo.log(e);
3590         /*
3591         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3592             if(t == this.activeItem && t.shouldDeactivate(e)){
3593                 this.activeItem.deactivate();
3594                 delete this.activeItem;
3595                 return;
3596             }
3597             if(t.canActivate){
3598                 this.setActiveItem(t, true);
3599             }
3600             return;
3601             
3602             
3603         }
3604         */
3605        
3606         Roo.log('pass click event');
3607         
3608         t.onClick(e);
3609         
3610         this.fireEvent("click", this, t, e);
3611         
3612         var _this = this;
3613         
3614         if(!t.href.length || t.href == '#'){
3615             (function() { _this.hide(); }).defer(100);
3616         }
3617         
3618     },
3619     
3620     onMouseOver : function(e){
3621         var t  = this.findTargetItem(e);
3622         //Roo.log(t);
3623         //if(t){
3624         //    if(t.canActivate && !t.disabled){
3625         //        this.setActiveItem(t, true);
3626         //    }
3627         //}
3628         
3629         this.fireEvent("mouseover", this, e, t);
3630     },
3631     isVisible : function(){
3632         return !this.hidden;
3633     },
3634     onMouseOut : function(e){
3635         var t  = this.findTargetItem(e);
3636         
3637         //if(t ){
3638         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3639         //        this.activeItem.deactivate();
3640         //        delete this.activeItem;
3641         //    }
3642         //}
3643         this.fireEvent("mouseout", this, e, t);
3644     },
3645     
3646     
3647     /**
3648      * Displays this menu relative to another element
3649      * @param {String/HTMLElement/Roo.Element} element The element to align to
3650      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3651      * the element (defaults to this.defaultAlign)
3652      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3653      */
3654     show : function(el, pos, parentMenu)
3655     {
3656         if (false === this.fireEvent("beforeshow", this)) {
3657             Roo.log("show canceled");
3658             return;
3659         }
3660         this.parentMenu = parentMenu;
3661         if(!this.el){
3662             this.render();
3663         }
3664         
3665         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3666     },
3667      /**
3668      * Displays this menu at a specific xy position
3669      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3670      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3671      */
3672     showAt : function(xy, parentMenu, /* private: */_e){
3673         this.parentMenu = parentMenu;
3674         if(!this.el){
3675             this.render();
3676         }
3677         if(_e !== false){
3678             this.fireEvent("beforeshow", this);
3679             //xy = this.el.adjustForConstraints(xy);
3680         }
3681         
3682         //this.el.show();
3683         this.hideMenuItems();
3684         this.hidden = false;
3685         this.triggerEl.addClass('open');
3686         this.el.addClass('show');
3687         
3688         // reassign x when hitting right
3689         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3690             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3691         }
3692         
3693         // reassign y when hitting bottom
3694         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3695             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3696         }
3697         
3698         // but the list may align on trigger left or trigger top... should it be a properity?
3699         
3700         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3701             this.el.setXY(xy);
3702         }
3703         
3704         this.focus();
3705         this.fireEvent("show", this);
3706     },
3707     
3708     focus : function(){
3709         return;
3710         if(!this.hidden){
3711             this.doFocus.defer(50, this);
3712         }
3713     },
3714
3715     doFocus : function(){
3716         if(!this.hidden){
3717             this.focusEl.focus();
3718         }
3719     },
3720
3721     /**
3722      * Hides this menu and optionally all parent menus
3723      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3724      */
3725     hide : function(deep)
3726     {
3727         if (false === this.fireEvent("beforehide", this)) {
3728             Roo.log("hide canceled");
3729             return;
3730         }
3731         this.hideMenuItems();
3732         if(this.el && this.isVisible()){
3733            
3734             if(this.activeItem){
3735                 this.activeItem.deactivate();
3736                 this.activeItem = null;
3737             }
3738             this.triggerEl.removeClass('open');;
3739             this.el.removeClass('show');
3740             this.hidden = true;
3741             this.fireEvent("hide", this);
3742         }
3743         if(deep === true && this.parentMenu){
3744             this.parentMenu.hide(true);
3745         }
3746     },
3747     
3748     onTriggerClick : function(e)
3749     {
3750         Roo.log('trigger click');
3751         
3752         var target = e.getTarget();
3753         
3754         Roo.log(target.nodeName.toLowerCase());
3755         
3756         if(target.nodeName.toLowerCase() === 'i'){
3757             e.preventDefault();
3758         }
3759         
3760     },
3761     
3762     onTriggerPress  : function(e)
3763     {
3764         Roo.log('trigger press');
3765         //Roo.log(e.getTarget());
3766        // Roo.log(this.triggerEl.dom);
3767        
3768         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3769         var pel = Roo.get(e.getTarget());
3770         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3771             Roo.log('is treeview or dropdown?');
3772             return;
3773         }
3774         
3775         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3776             return;
3777         }
3778         
3779         if (this.isVisible()) {
3780             Roo.log('hide');
3781             this.hide();
3782         } else {
3783             Roo.log('show');
3784             this.show(this.triggerEl, '?', false);
3785         }
3786         
3787         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3788             e.stopEvent();
3789         }
3790         
3791     },
3792        
3793     
3794     hideMenuItems : function()
3795     {
3796         Roo.log("hide Menu Items");
3797         if (!this.el) { 
3798             return;
3799         }
3800         
3801         this.el.select('.open',true).each(function(aa) {
3802             
3803             aa.removeClass('open');
3804          
3805         });
3806     },
3807     addxtypeChild : function (tree, cntr) {
3808         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3809           
3810         this.menuitems.add(comp);
3811         return comp;
3812
3813     },
3814     getEl : function()
3815     {
3816         Roo.log(this.el);
3817         return this.el;
3818     },
3819     
3820     clear : function()
3821     {
3822         this.getEl().dom.innerHTML = '';
3823         this.menuitems.clear();
3824     }
3825 });
3826
3827  
3828  /*
3829  * - LGPL
3830  *
3831  * menu item
3832  * 
3833  */
3834
3835
3836 /**
3837  * @class Roo.bootstrap.MenuItem
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap MenuItem class
3840  * @cfg {String} html the menu label
3841  * @cfg {String} href the link
3842  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3843  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3844  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3845  * @cfg {String} fa favicon to show on left of menu item.
3846  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3847  * 
3848  * 
3849  * @constructor
3850  * Create a new MenuItem
3851  * @param {Object} config The config object
3852  */
3853
3854
3855 Roo.bootstrap.MenuItem = function(config){
3856     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3857     this.addEvents({
3858         // raw events
3859         /**
3860          * @event click
3861          * The raw click event for the entire grid.
3862          * @param {Roo.bootstrap.MenuItem} this
3863          * @param {Roo.EventObject} e
3864          */
3865         "click" : true
3866     });
3867 };
3868
3869 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3870     
3871     href : false,
3872     html : false,
3873     preventDefault: false,
3874     isContainer : false,
3875     active : false,
3876     fa: false,
3877     
3878     getAutoCreate : function(){
3879         
3880         if(this.isContainer){
3881             return {
3882                 tag: 'li',
3883                 cls: 'dropdown-menu-item '
3884             };
3885         }
3886         var ctag = {
3887             tag: 'span',
3888             html: 'Link'
3889         };
3890         
3891         var anc = {
3892             tag : 'a',
3893             cls : 'dropdown-item',
3894             href : '#',
3895             cn : [  ]
3896         };
3897         
3898         if (this.fa !== false) {
3899             anc.cn.push({
3900                 tag : 'i',
3901                 cls : 'fa fa-' + this.fa
3902             });
3903         }
3904         
3905         anc.cn.push(ctag);
3906         
3907         
3908         var cfg= {
3909             tag: 'li',
3910             cls: 'dropdown-menu-item',
3911             cn: [ anc ]
3912         };
3913         if (this.parent().type == 'treeview') {
3914             cfg.cls = 'treeview-menu';
3915         }
3916         if (this.active) {
3917             cfg.cls += ' active';
3918         }
3919         
3920         
3921         
3922         anc.href = this.href || cfg.cn[0].href ;
3923         ctag.html = this.html || cfg.cn[0].html ;
3924         return cfg;
3925     },
3926     
3927     initEvents: function()
3928     {
3929         if (this.parent().type == 'treeview') {
3930             this.el.select('a').on('click', this.onClick, this);
3931         }
3932         
3933         if (this.menu) {
3934             this.menu.parentType = this.xtype;
3935             this.menu.triggerEl = this.el;
3936             this.menu = this.addxtype(Roo.apply({}, this.menu));
3937         }
3938         
3939     },
3940     onClick : function(e)
3941     {
3942         Roo.log('item on click ');
3943         
3944         if(this.preventDefault){
3945             e.preventDefault();
3946         }
3947         //this.parent().hideMenuItems();
3948         
3949         this.fireEvent('click', this, e);
3950     },
3951     getEl : function()
3952     {
3953         return this.el;
3954     } 
3955 });
3956
3957  
3958
3959  /*
3960  * - LGPL
3961  *
3962  * menu separator
3963  * 
3964  */
3965
3966
3967 /**
3968  * @class Roo.bootstrap.MenuSeparator
3969  * @extends Roo.bootstrap.Component
3970  * Bootstrap MenuSeparator class
3971  * 
3972  * @constructor
3973  * Create a new MenuItem
3974  * @param {Object} config The config object
3975  */
3976
3977
3978 Roo.bootstrap.MenuSeparator = function(config){
3979     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3980 };
3981
3982 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3983     
3984     getAutoCreate : function(){
3985         var cfg = {
3986             cls: 'divider',
3987             tag : 'li'
3988         };
3989         
3990         return cfg;
3991     }
3992    
3993 });
3994
3995  
3996
3997  
3998 /*
3999 * Licence: LGPL
4000 */
4001
4002 /**
4003  * @class Roo.bootstrap.Modal
4004  * @extends Roo.bootstrap.Component
4005  * Bootstrap Modal class
4006  * @cfg {String} title Title of dialog
4007  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4008  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4009  * @cfg {Boolean} specificTitle default false
4010  * @cfg {Array} buttons Array of buttons or standard button set..
4011  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4012  * @cfg {Boolean} animate default true
4013  * @cfg {Boolean} allow_close default true
4014  * @cfg {Boolean} fitwindow default false
4015  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4016  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4017  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4018  * @cfg {String} size (sm|lg|xl) default empty
4019  * @cfg {Number} max_width set the max width of modal
4020  * @cfg {Boolean} editableTitle can the title be edited
4021
4022  *
4023  *
4024  * @constructor
4025  * Create a new Modal Dialog
4026  * @param {Object} config The config object
4027  */
4028
4029 Roo.bootstrap.Modal = function(config){
4030     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4031     this.addEvents({
4032         // raw events
4033         /**
4034          * @event btnclick
4035          * The raw btnclick event for the button
4036          * @param {Roo.EventObject} e
4037          */
4038         "btnclick" : true,
4039         /**
4040          * @event resize
4041          * Fire when dialog resize
4042          * @param {Roo.bootstrap.Modal} this
4043          * @param {Roo.EventObject} e
4044          */
4045         "resize" : true,
4046         /**
4047          * @event titlechanged
4048          * Fire when the editable title has been changed
4049          * @param {Roo.bootstrap.Modal} this
4050          * @param {Roo.EventObject} value
4051          */
4052         "titlechanged" : true 
4053         
4054     });
4055     this.buttons = this.buttons || [];
4056
4057     if (this.tmpl) {
4058         this.tmpl = Roo.factory(this.tmpl);
4059     }
4060
4061 };
4062
4063 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4064
4065     title : 'test dialog',
4066
4067     buttons : false,
4068
4069     // set on load...
4070
4071     html: false,
4072
4073     tmp: false,
4074
4075     specificTitle: false,
4076
4077     buttonPosition: 'right',
4078
4079     allow_close : true,
4080
4081     animate : true,
4082
4083     fitwindow: false,
4084     
4085      // private
4086     dialogEl: false,
4087     bodyEl:  false,
4088     footerEl:  false,
4089     titleEl:  false,
4090     closeEl:  false,
4091
4092     size: '',
4093     
4094     max_width: 0,
4095     
4096     max_height: 0,
4097     
4098     fit_content: false,
4099     editableTitle  : false,
4100
4101     onRender : function(ct, position)
4102     {
4103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4104
4105         if(!this.el){
4106             var cfg = Roo.apply({},  this.getAutoCreate());
4107             cfg.id = Roo.id();
4108             //if(!cfg.name){
4109             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4110             //}
4111             //if (!cfg.name.length) {
4112             //    delete cfg.name;
4113            // }
4114             if (this.cls) {
4115                 cfg.cls += ' ' + this.cls;
4116             }
4117             if (this.style) {
4118                 cfg.style = this.style;
4119             }
4120             this.el = Roo.get(document.body).createChild(cfg, position);
4121         }
4122         //var type = this.el.dom.type;
4123
4124
4125         if(this.tabIndex !== undefined){
4126             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4127         }
4128
4129         this.dialogEl = this.el.select('.modal-dialog',true).first();
4130         this.bodyEl = this.el.select('.modal-body',true).first();
4131         this.closeEl = this.el.select('.modal-header .close', true).first();
4132         this.headerEl = this.el.select('.modal-header',true).first();
4133         this.titleEl = this.el.select('.modal-title',true).first();
4134         this.footerEl = this.el.select('.modal-footer',true).first();
4135
4136         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4137         
4138         //this.el.addClass("x-dlg-modal");
4139
4140         if (this.buttons.length) {
4141             Roo.each(this.buttons, function(bb) {
4142                 var b = Roo.apply({}, bb);
4143                 b.xns = b.xns || Roo.bootstrap;
4144                 b.xtype = b.xtype || 'Button';
4145                 if (typeof(b.listeners) == 'undefined') {
4146                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4147                 }
4148
4149                 var btn = Roo.factory(b);
4150
4151                 btn.render(this.getButtonContainer());
4152
4153             },this);
4154         }
4155         // render the children.
4156         var nitems = [];
4157
4158         if(typeof(this.items) != 'undefined'){
4159             var items = this.items;
4160             delete this.items;
4161
4162             for(var i =0;i < items.length;i++) {
4163                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4164             }
4165         }
4166
4167         this.items = nitems;
4168
4169         // where are these used - they used to be body/close/footer
4170
4171
4172         this.initEvents();
4173         //this.el.addClass([this.fieldClass, this.cls]);
4174
4175     },
4176
4177     getAutoCreate : function()
4178     {
4179         // we will default to modal-body-overflow - might need to remove or make optional later.
4180         var bdy = {
4181                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4182                 html : this.html || ''
4183         };
4184
4185         var title = {
4186             tag: 'h5',
4187             cls : 'modal-title',
4188             html : this.title
4189         };
4190
4191         if(this.specificTitle){ // WTF is this?
4192             title = this.title;
4193         }
4194
4195         var header = [];
4196         if (this.allow_close && Roo.bootstrap.version == 3) {
4197             header.push({
4198                 tag: 'button',
4199                 cls : 'close',
4200                 html : '&times'
4201             });
4202         }
4203
4204         header.push(title);
4205
4206         if (this.editableTitle) {
4207             header.push({
4208                 cls: 'form-control roo-editable-title d-none',
4209                 tag: 'input',
4210                 type: 'text'
4211             });
4212         }
4213         
4214         if (this.allow_close && Roo.bootstrap.version == 4) {
4215             header.push({
4216                 tag: 'button',
4217                 cls : 'close',
4218                 html : '&times'
4219             });
4220         }
4221         
4222         var size = '';
4223
4224         if(this.size.length){
4225             size = 'modal-' + this.size;
4226         }
4227         
4228         var footer = Roo.bootstrap.version == 3 ?
4229             {
4230                 cls : 'modal-footer',
4231                 cn : [
4232                     {
4233                         tag: 'div',
4234                         cls: 'btn-' + this.buttonPosition
4235                     }
4236                 ]
4237
4238             } :
4239             {  // BS4 uses mr-auto on left buttons....
4240                 cls : 'modal-footer'
4241             };
4242
4243             
4244
4245         
4246         
4247         var modal = {
4248             cls: "modal",
4249              cn : [
4250                 {
4251                     cls: "modal-dialog " + size,
4252                     cn : [
4253                         {
4254                             cls : "modal-content",
4255                             cn : [
4256                                 {
4257                                     cls : 'modal-header',
4258                                     cn : header
4259                                 },
4260                                 bdy,
4261                                 footer
4262                             ]
4263
4264                         }
4265                     ]
4266
4267                 }
4268             ]
4269         };
4270
4271         if(this.animate){
4272             modal.cls += ' fade';
4273         }
4274
4275         return modal;
4276
4277     },
4278     getChildContainer : function() {
4279
4280          return this.bodyEl;
4281
4282     },
4283     getButtonContainer : function() {
4284         
4285          return Roo.bootstrap.version == 4 ?
4286             this.el.select('.modal-footer',true).first()
4287             : this.el.select('.modal-footer div',true).first();
4288
4289     },
4290     initEvents : function()
4291     {
4292         if (this.allow_close) {
4293             this.closeEl.on('click', this.hide, this);
4294         }
4295         Roo.EventManager.onWindowResize(this.resize, this, true);
4296         if (this.editableTitle) {
4297             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4298             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4299             this.headerEditEl.on('keyup', function(e) {
4300                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4301                         this.toggleHeaderInput(false)
4302                     }
4303                 }, this);
4304             this.headerEditEl.on('blur', function(e) {
4305                 this.toggleHeaderInput(false)
4306             },this);
4307         }
4308
4309     },
4310   
4311
4312     resize : function()
4313     {
4314         this.maskEl.setSize(
4315             Roo.lib.Dom.getViewWidth(true),
4316             Roo.lib.Dom.getViewHeight(true)
4317         );
4318         
4319         if (this.fitwindow) {
4320             
4321            this.dialogEl.setStyle( { 'max-width' : '100%' });
4322             this.setSize(
4323                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4324                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4325             );
4326             return;
4327         }
4328         
4329         if(this.max_width !== 0) {
4330             
4331             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4332             
4333             if(this.height) {
4334                 this.setSize(w, this.height);
4335                 return;
4336             }
4337             
4338             if(this.max_height) {
4339                 this.setSize(w,Math.min(
4340                     this.max_height,
4341                     Roo.lib.Dom.getViewportHeight(true) - 60
4342                 ));
4343                 
4344                 return;
4345             }
4346             
4347             if(!this.fit_content) {
4348                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4349                 return;
4350             }
4351             
4352             this.setSize(w, Math.min(
4353                 60 +
4354                 this.headerEl.getHeight() + 
4355                 this.footerEl.getHeight() + 
4356                 this.getChildHeight(this.bodyEl.dom.childNodes),
4357                 Roo.lib.Dom.getViewportHeight(true) - 60)
4358             );
4359         }
4360         
4361     },
4362
4363     setSize : function(w,h)
4364     {
4365         if (!w && !h) {
4366             return;
4367         }
4368         
4369         this.resizeTo(w,h);
4370     },
4371
4372     show : function() {
4373
4374         if (!this.rendered) {
4375             this.render();
4376         }
4377         this.toggleHeaderInput(false);
4378         //this.el.setStyle('display', 'block');
4379         this.el.removeClass('hideing');
4380         this.el.dom.style.display='block';
4381         
4382         Roo.get(document.body).addClass('modal-open');
4383  
4384         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4385             
4386             (function(){
4387                 this.el.addClass('show');
4388                 this.el.addClass('in');
4389             }).defer(50, this);
4390         }else{
4391             this.el.addClass('show');
4392             this.el.addClass('in');
4393         }
4394
4395         // not sure how we can show data in here..
4396         //if (this.tmpl) {
4397         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4398         //}
4399
4400         Roo.get(document.body).addClass("x-body-masked");
4401         
4402         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4403         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4404         this.maskEl.dom.style.display = 'block';
4405         this.maskEl.addClass('show');
4406         
4407         
4408         this.resize();
4409         
4410         this.fireEvent('show', this);
4411
4412         // set zindex here - otherwise it appears to be ignored...
4413         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4414
4415         (function () {
4416             this.items.forEach( function(e) {
4417                 e.layout ? e.layout() : false;
4418
4419             });
4420         }).defer(100,this);
4421
4422     },
4423     hide : function()
4424     {
4425         if(this.fireEvent("beforehide", this) !== false){
4426             
4427             this.maskEl.removeClass('show');
4428             
4429             this.maskEl.dom.style.display = '';
4430             Roo.get(document.body).removeClass("x-body-masked");
4431             this.el.removeClass('in');
4432             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4433
4434             if(this.animate){ // why
4435                 this.el.addClass('hideing');
4436                 this.el.removeClass('show');
4437                 (function(){
4438                     if (!this.el.hasClass('hideing')) {
4439                         return; // it's been shown again...
4440                     }
4441                     
4442                     this.el.dom.style.display='';
4443
4444                     Roo.get(document.body).removeClass('modal-open');
4445                     this.el.removeClass('hideing');
4446                 }).defer(150,this);
4447                 
4448             }else{
4449                 this.el.removeClass('show');
4450                 this.el.dom.style.display='';
4451                 Roo.get(document.body).removeClass('modal-open');
4452
4453             }
4454             this.fireEvent('hide', this);
4455         }
4456     },
4457     isVisible : function()
4458     {
4459         
4460         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4461         
4462     },
4463
4464     addButton : function(str, cb)
4465     {
4466
4467
4468         var b = Roo.apply({}, { html : str } );
4469         b.xns = b.xns || Roo.bootstrap;
4470         b.xtype = b.xtype || 'Button';
4471         if (typeof(b.listeners) == 'undefined') {
4472             b.listeners = { click : cb.createDelegate(this)  };
4473         }
4474
4475         var btn = Roo.factory(b);
4476
4477         btn.render(this.getButtonContainer());
4478
4479         return btn;
4480
4481     },
4482
4483     setDefaultButton : function(btn)
4484     {
4485         //this.el.select('.modal-footer').()
4486     },
4487
4488     resizeTo: function(w,h)
4489     {
4490         this.dialogEl.setWidth(w);
4491         
4492         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4493
4494         this.bodyEl.setHeight(h - diff);
4495         
4496         this.fireEvent('resize', this);
4497     },
4498     
4499     setContentSize  : function(w, h)
4500     {
4501
4502     },
4503     onButtonClick: function(btn,e)
4504     {
4505         //Roo.log([a,b,c]);
4506         this.fireEvent('btnclick', btn.name, e);
4507     },
4508      /**
4509      * Set the title of the Dialog
4510      * @param {String} str new Title
4511      */
4512     setTitle: function(str) {
4513         this.titleEl.dom.innerHTML = str;
4514         this.title = str;
4515     },
4516     /**
4517      * Set the body of the Dialog
4518      * @param {String} str new Title
4519      */
4520     setBody: function(str) {
4521         this.bodyEl.dom.innerHTML = str;
4522     },
4523     /**
4524      * Set the body of the Dialog using the template
4525      * @param {Obj} data - apply this data to the template and replace the body contents.
4526      */
4527     applyBody: function(obj)
4528     {
4529         if (!this.tmpl) {
4530             Roo.log("Error - using apply Body without a template");
4531             //code
4532         }
4533         this.tmpl.overwrite(this.bodyEl, obj);
4534     },
4535     
4536     getChildHeight : function(child_nodes)
4537     {
4538         if(
4539             !child_nodes ||
4540             child_nodes.length == 0
4541         ) {
4542             return 0;
4543         }
4544         
4545         var child_height = 0;
4546         
4547         for(var i = 0; i < child_nodes.length; i++) {
4548             
4549             /*
4550             * for modal with tabs...
4551             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4552                 
4553                 var layout_childs = child_nodes[i].childNodes;
4554                 
4555                 for(var j = 0; j < layout_childs.length; j++) {
4556                     
4557                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4558                         
4559                         var layout_body_childs = layout_childs[j].childNodes;
4560                         
4561                         for(var k = 0; k < layout_body_childs.length; k++) {
4562                             
4563                             if(layout_body_childs[k].classList.contains('navbar')) {
4564                                 child_height += layout_body_childs[k].offsetHeight;
4565                                 continue;
4566                             }
4567                             
4568                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4569                                 
4570                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4571                                 
4572                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4573                                     
4574                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4575                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4576                                         continue;
4577                                     }
4578                                     
4579                                 }
4580                                 
4581                             }
4582                             
4583                         }
4584                     }
4585                 }
4586                 continue;
4587             }
4588             */
4589             
4590             child_height += child_nodes[i].offsetHeight;
4591             // Roo.log(child_nodes[i].offsetHeight);
4592         }
4593         
4594         return child_height;
4595     },
4596     toggleHeaderInput : function(is_edit)
4597     {
4598         if (!this.editableTitle) {
4599             return; // not editable.
4600         }
4601         if (is_edit && this.is_header_editing) {
4602             return; // already editing..
4603         }
4604         if (is_edit) {
4605     
4606             this.headerEditEl.dom.value = this.title;
4607             this.headerEditEl.removeClass('d-none');
4608             this.headerEditEl.dom.focus();
4609             this.titleEl.addClass('d-none');
4610             
4611             this.is_header_editing = true;
4612             return
4613         }
4614         // flip back to not editing.
4615         this.title = this.headerEditEl.dom.value;
4616         this.headerEditEl.addClass('d-none');
4617         this.titleEl.removeClass('d-none');
4618         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4619         this.is_header_editing = false;
4620         this.fireEvent('titlechanged', this, this.title);
4621     
4622             
4623         
4624     }
4625
4626 });
4627
4628
4629 Roo.apply(Roo.bootstrap.Modal,  {
4630     /**
4631          * Button config that displays a single OK button
4632          * @type Object
4633          */
4634         OK :  [{
4635             name : 'ok',
4636             weight : 'primary',
4637             html : 'OK'
4638         }],
4639         /**
4640          * Button config that displays Yes and No buttons
4641          * @type Object
4642          */
4643         YESNO : [
4644             {
4645                 name  : 'no',
4646                 html : 'No'
4647             },
4648             {
4649                 name  :'yes',
4650                 weight : 'primary',
4651                 html : 'Yes'
4652             }
4653         ],
4654
4655         /**
4656          * Button config that displays OK and Cancel buttons
4657          * @type Object
4658          */
4659         OKCANCEL : [
4660             {
4661                name : 'cancel',
4662                 html : 'Cancel'
4663             },
4664             {
4665                 name : 'ok',
4666                 weight : 'primary',
4667                 html : 'OK'
4668             }
4669         ],
4670         /**
4671          * Button config that displays Yes, No and Cancel buttons
4672          * @type Object
4673          */
4674         YESNOCANCEL : [
4675             {
4676                 name : 'yes',
4677                 weight : 'primary',
4678                 html : 'Yes'
4679             },
4680             {
4681                 name : 'no',
4682                 html : 'No'
4683             },
4684             {
4685                 name : 'cancel',
4686                 html : 'Cancel'
4687             }
4688         ],
4689         
4690         zIndex : 10001
4691 });
4692
4693 /*
4694  * - LGPL
4695  *
4696  * messagebox - can be used as a replace
4697  * 
4698  */
4699 /**
4700  * @class Roo.MessageBox
4701  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4702  * Example usage:
4703  *<pre><code>
4704 // Basic alert:
4705 Roo.Msg.alert('Status', 'Changes saved successfully.');
4706
4707 // Prompt for user data:
4708 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4709     if (btn == 'ok'){
4710         // process text value...
4711     }
4712 });
4713
4714 // Show a dialog using config options:
4715 Roo.Msg.show({
4716    title:'Save Changes?',
4717    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4718    buttons: Roo.Msg.YESNOCANCEL,
4719    fn: processResult,
4720    animEl: 'elId'
4721 });
4722 </code></pre>
4723  * @singleton
4724  */
4725 Roo.bootstrap.MessageBox = function(){
4726     var dlg, opt, mask, waitTimer;
4727     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4728     var buttons, activeTextEl, bwidth;
4729
4730     
4731     // private
4732     var handleButton = function(button){
4733         dlg.hide();
4734         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4735     };
4736
4737     // private
4738     var handleHide = function(){
4739         if(opt && opt.cls){
4740             dlg.el.removeClass(opt.cls);
4741         }
4742         //if(waitTimer){
4743         //    Roo.TaskMgr.stop(waitTimer);
4744         //    waitTimer = null;
4745         //}
4746     };
4747
4748     // private
4749     var updateButtons = function(b){
4750         var width = 0;
4751         if(!b){
4752             buttons["ok"].hide();
4753             buttons["cancel"].hide();
4754             buttons["yes"].hide();
4755             buttons["no"].hide();
4756             dlg.footerEl.hide();
4757             
4758             return width;
4759         }
4760         dlg.footerEl.show();
4761         for(var k in buttons){
4762             if(typeof buttons[k] != "function"){
4763                 if(b[k]){
4764                     buttons[k].show();
4765                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4766                     width += buttons[k].el.getWidth()+15;
4767                 }else{
4768                     buttons[k].hide();
4769                 }
4770             }
4771         }
4772         return width;
4773     };
4774
4775     // private
4776     var handleEsc = function(d, k, e){
4777         if(opt && opt.closable !== false){
4778             dlg.hide();
4779         }
4780         if(e){
4781             e.stopEvent();
4782         }
4783     };
4784
4785     return {
4786         /**
4787          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4788          * @return {Roo.BasicDialog} The BasicDialog element
4789          */
4790         getDialog : function(){
4791            if(!dlg){
4792                 dlg = new Roo.bootstrap.Modal( {
4793                     //draggable: true,
4794                     //resizable:false,
4795                     //constraintoviewport:false,
4796                     //fixedcenter:true,
4797                     //collapsible : false,
4798                     //shim:true,
4799                     //modal: true,
4800                 //    width: 'auto',
4801                   //  height:100,
4802                     //buttonAlign:"center",
4803                     closeClick : function(){
4804                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4805                             handleButton("no");
4806                         }else{
4807                             handleButton("cancel");
4808                         }
4809                     }
4810                 });
4811                 dlg.render();
4812                 dlg.on("hide", handleHide);
4813                 mask = dlg.mask;
4814                 //dlg.addKeyListener(27, handleEsc);
4815                 buttons = {};
4816                 this.buttons = buttons;
4817                 var bt = this.buttonText;
4818                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4819                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4820                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4821                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4822                 //Roo.log(buttons);
4823                 bodyEl = dlg.bodyEl.createChild({
4824
4825                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4826                         '<textarea class="roo-mb-textarea"></textarea>' +
4827                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4828                 });
4829                 msgEl = bodyEl.dom.firstChild;
4830                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4831                 textboxEl.enableDisplayMode();
4832                 textboxEl.addKeyListener([10,13], function(){
4833                     if(dlg.isVisible() && opt && opt.buttons){
4834                         if(opt.buttons.ok){
4835                             handleButton("ok");
4836                         }else if(opt.buttons.yes){
4837                             handleButton("yes");
4838                         }
4839                     }
4840                 });
4841                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4842                 textareaEl.enableDisplayMode();
4843                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4844                 progressEl.enableDisplayMode();
4845                 
4846                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4847                 var pf = progressEl.dom.firstChild;
4848                 if (pf) {
4849                     pp = Roo.get(pf.firstChild);
4850                     pp.setHeight(pf.offsetHeight);
4851                 }
4852                 
4853             }
4854             return dlg;
4855         },
4856
4857         /**
4858          * Updates the message box body text
4859          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4860          * the XHTML-compliant non-breaking space character '&amp;#160;')
4861          * @return {Roo.MessageBox} This message box
4862          */
4863         updateText : function(text)
4864         {
4865             if(!dlg.isVisible() && !opt.width){
4866                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4867                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4868             }
4869             msgEl.innerHTML = text || '&#160;';
4870       
4871             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4872             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4873             var w = Math.max(
4874                     Math.min(opt.width || cw , this.maxWidth), 
4875                     Math.max(opt.minWidth || this.minWidth, bwidth)
4876             );
4877             if(opt.prompt){
4878                 activeTextEl.setWidth(w);
4879             }
4880             if(dlg.isVisible()){
4881                 dlg.fixedcenter = false;
4882             }
4883             // to big, make it scroll. = But as usual stupid IE does not support
4884             // !important..
4885             
4886             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4887                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4888                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4889             } else {
4890                 bodyEl.dom.style.height = '';
4891                 bodyEl.dom.style.overflowY = '';
4892             }
4893             if (cw > w) {
4894                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4895             } else {
4896                 bodyEl.dom.style.overflowX = '';
4897             }
4898             
4899             dlg.setContentSize(w, bodyEl.getHeight());
4900             if(dlg.isVisible()){
4901                 dlg.fixedcenter = true;
4902             }
4903             return this;
4904         },
4905
4906         /**
4907          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4908          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4909          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4910          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4911          * @return {Roo.MessageBox} This message box
4912          */
4913         updateProgress : function(value, text){
4914             if(text){
4915                 this.updateText(text);
4916             }
4917             
4918             if (pp) { // weird bug on my firefox - for some reason this is not defined
4919                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4920                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4921             }
4922             return this;
4923         },        
4924
4925         /**
4926          * Returns true if the message box is currently displayed
4927          * @return {Boolean} True if the message box is visible, else false
4928          */
4929         isVisible : function(){
4930             return dlg && dlg.isVisible();  
4931         },
4932
4933         /**
4934          * Hides the message box if it is displayed
4935          */
4936         hide : function(){
4937             if(this.isVisible()){
4938                 dlg.hide();
4939             }  
4940         },
4941
4942         /**
4943          * Displays a new message box, or reinitializes an existing message box, based on the config options
4944          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4945          * The following config object properties are supported:
4946          * <pre>
4947 Property    Type             Description
4948 ----------  ---------------  ------------------------------------------------------------------------------------
4949 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4950                                    closes (defaults to undefined)
4951 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4952                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4953 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4954                                    progress and wait dialogs will ignore this property and always hide the
4955                                    close button as they can only be closed programmatically.
4956 cls               String           A custom CSS class to apply to the message box element
4957 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4958                                    displayed (defaults to 75)
4959 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4960                                    function will be btn (the name of the button that was clicked, if applicable,
4961                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4962                                    Progress and wait dialogs will ignore this option since they do not respond to
4963                                    user actions and can only be closed programmatically, so any required function
4964                                    should be called by the same code after it closes the dialog.
4965 icon              String           A CSS class that provides a background image to be used as an icon for
4966                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4967 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4968 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4969 modal             Boolean          False to allow user interaction with the page while the message box is
4970                                    displayed (defaults to true)
4971 msg               String           A string that will replace the existing message box body text (defaults
4972                                    to the XHTML-compliant non-breaking space character '&#160;')
4973 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4974 progress          Boolean          True to display a progress bar (defaults to false)
4975 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4976 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4977 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4978 title             String           The title text
4979 value             String           The string value to set into the active textbox element if displayed
4980 wait              Boolean          True to display a progress bar (defaults to false)
4981 width             Number           The width of the dialog in pixels
4982 </pre>
4983          *
4984          * Example usage:
4985          * <pre><code>
4986 Roo.Msg.show({
4987    title: 'Address',
4988    msg: 'Please enter your address:',
4989    width: 300,
4990    buttons: Roo.MessageBox.OKCANCEL,
4991    multiline: true,
4992    fn: saveAddress,
4993    animEl: 'addAddressBtn'
4994 });
4995 </code></pre>
4996          * @param {Object} config Configuration options
4997          * @return {Roo.MessageBox} This message box
4998          */
4999         show : function(options)
5000         {
5001             
5002             // this causes nightmares if you show one dialog after another
5003             // especially on callbacks..
5004              
5005             if(this.isVisible()){
5006                 
5007                 this.hide();
5008                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5009                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5010                 Roo.log("New Dialog Message:" +  options.msg )
5011                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5012                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5013                 
5014             }
5015             var d = this.getDialog();
5016             opt = options;
5017             d.setTitle(opt.title || "&#160;");
5018             d.closeEl.setDisplayed(opt.closable !== false);
5019             activeTextEl = textboxEl;
5020             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5021             if(opt.prompt){
5022                 if(opt.multiline){
5023                     textboxEl.hide();
5024                     textareaEl.show();
5025                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5026                         opt.multiline : this.defaultTextHeight);
5027                     activeTextEl = textareaEl;
5028                 }else{
5029                     textboxEl.show();
5030                     textareaEl.hide();
5031                 }
5032             }else{
5033                 textboxEl.hide();
5034                 textareaEl.hide();
5035             }
5036             progressEl.setDisplayed(opt.progress === true);
5037             if (opt.progress) {
5038                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5039             }
5040             this.updateProgress(0);
5041             activeTextEl.dom.value = opt.value || "";
5042             if(opt.prompt){
5043                 dlg.setDefaultButton(activeTextEl);
5044             }else{
5045                 var bs = opt.buttons;
5046                 var db = null;
5047                 if(bs && bs.ok){
5048                     db = buttons["ok"];
5049                 }else if(bs && bs.yes){
5050                     db = buttons["yes"];
5051                 }
5052                 dlg.setDefaultButton(db);
5053             }
5054             bwidth = updateButtons(opt.buttons);
5055             this.updateText(opt.msg);
5056             if(opt.cls){
5057                 d.el.addClass(opt.cls);
5058             }
5059             d.proxyDrag = opt.proxyDrag === true;
5060             d.modal = opt.modal !== false;
5061             d.mask = opt.modal !== false ? mask : false;
5062             if(!d.isVisible()){
5063                 // force it to the end of the z-index stack so it gets a cursor in FF
5064                 document.body.appendChild(dlg.el.dom);
5065                 d.animateTarget = null;
5066                 d.show(options.animEl);
5067             }
5068             return this;
5069         },
5070
5071         /**
5072          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5073          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5074          * and closing the message box when the process is complete.
5075          * @param {String} title The title bar text
5076          * @param {String} msg The message box body text
5077          * @return {Roo.MessageBox} This message box
5078          */
5079         progress : function(title, msg){
5080             this.show({
5081                 title : title,
5082                 msg : msg,
5083                 buttons: false,
5084                 progress:true,
5085                 closable:false,
5086                 minWidth: this.minProgressWidth,
5087                 modal : true
5088             });
5089             return this;
5090         },
5091
5092         /**
5093          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5094          * If a callback function is passed it will be called after the user clicks the button, and the
5095          * id of the button that was clicked will be passed as the only parameter to the callback
5096          * (could also be the top-right close button).
5097          * @param {String} title The title bar text
5098          * @param {String} msg The message box body text
5099          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5100          * @param {Object} scope (optional) The scope of the callback function
5101          * @return {Roo.MessageBox} This message box
5102          */
5103         alert : function(title, msg, fn, scope)
5104         {
5105             this.show({
5106                 title : title,
5107                 msg : msg,
5108                 buttons: this.OK,
5109                 fn: fn,
5110                 closable : false,
5111                 scope : scope,
5112                 modal : true
5113             });
5114             return this;
5115         },
5116
5117         /**
5118          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5119          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5120          * You are responsible for closing the message box when the process is complete.
5121          * @param {String} msg The message box body text
5122          * @param {String} title (optional) The title bar text
5123          * @return {Roo.MessageBox} This message box
5124          */
5125         wait : function(msg, title){
5126             this.show({
5127                 title : title,
5128                 msg : msg,
5129                 buttons: false,
5130                 closable:false,
5131                 progress:true,
5132                 modal:true,
5133                 width:300,
5134                 wait:true
5135             });
5136             waitTimer = Roo.TaskMgr.start({
5137                 run: function(i){
5138                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5139                 },
5140                 interval: 1000
5141             });
5142             return this;
5143         },
5144
5145         /**
5146          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5147          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5148          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5149          * @param {String} title The title bar text
5150          * @param {String} msg The message box body text
5151          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5152          * @param {Object} scope (optional) The scope of the callback function
5153          * @return {Roo.MessageBox} This message box
5154          */
5155         confirm : function(title, msg, fn, scope){
5156             this.show({
5157                 title : title,
5158                 msg : msg,
5159                 buttons: this.YESNO,
5160                 fn: fn,
5161                 scope : scope,
5162                 modal : true
5163             });
5164             return this;
5165         },
5166
5167         /**
5168          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5169          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5170          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5171          * (could also be the top-right close button) and the text that was entered will be passed as the two
5172          * parameters to the callback.
5173          * @param {String} title The title bar text
5174          * @param {String} msg The message box body text
5175          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5176          * @param {Object} scope (optional) The scope of the callback function
5177          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5178          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         prompt : function(title, msg, fn, scope, multiline){
5182             this.show({
5183                 title : title,
5184                 msg : msg,
5185                 buttons: this.OKCANCEL,
5186                 fn: fn,
5187                 minWidth:250,
5188                 scope : scope,
5189                 prompt:true,
5190                 multiline: multiline,
5191                 modal : true
5192             });
5193             return this;
5194         },
5195
5196         /**
5197          * Button config that displays a single OK button
5198          * @type Object
5199          */
5200         OK : {ok:true},
5201         /**
5202          * Button config that displays Yes and No buttons
5203          * @type Object
5204          */
5205         YESNO : {yes:true, no:true},
5206         /**
5207          * Button config that displays OK and Cancel buttons
5208          * @type Object
5209          */
5210         OKCANCEL : {ok:true, cancel:true},
5211         /**
5212          * Button config that displays Yes, No and Cancel buttons
5213          * @type Object
5214          */
5215         YESNOCANCEL : {yes:true, no:true, cancel:true},
5216
5217         /**
5218          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5219          * @type Number
5220          */
5221         defaultTextHeight : 75,
5222         /**
5223          * The maximum width in pixels of the message box (defaults to 600)
5224          * @type Number
5225          */
5226         maxWidth : 600,
5227         /**
5228          * The minimum width in pixels of the message box (defaults to 100)
5229          * @type Number
5230          */
5231         minWidth : 100,
5232         /**
5233          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5234          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5235          * @type Number
5236          */
5237         minProgressWidth : 250,
5238         /**
5239          * An object containing the default button text strings that can be overriden for localized language support.
5240          * Supported properties are: ok, cancel, yes and no.
5241          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5242          * @type Object
5243          */
5244         buttonText : {
5245             ok : "OK",
5246             cancel : "Cancel",
5247             yes : "Yes",
5248             no : "No"
5249         }
5250     };
5251 }();
5252
5253 /**
5254  * Shorthand for {@link Roo.MessageBox}
5255  */
5256 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5257 Roo.Msg = Roo.Msg || Roo.MessageBox;
5258 /*
5259  * - LGPL
5260  *
5261  * navbar
5262  * 
5263  */
5264
5265 /**
5266  * @class Roo.bootstrap.Navbar
5267  * @extends Roo.bootstrap.Component
5268  * Bootstrap Navbar class
5269
5270  * @constructor
5271  * Create a new Navbar
5272  * @param {Object} config The config object
5273  */
5274
5275
5276 Roo.bootstrap.Navbar = function(config){
5277     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5278     this.addEvents({
5279         // raw events
5280         /**
5281          * @event beforetoggle
5282          * Fire before toggle the menu
5283          * @param {Roo.EventObject} e
5284          */
5285         "beforetoggle" : true
5286     });
5287 };
5288
5289 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5290     
5291     
5292    
5293     // private
5294     navItems : false,
5295     loadMask : false,
5296     
5297     
5298     getAutoCreate : function(){
5299         
5300         
5301         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5302         
5303     },
5304     
5305     initEvents :function ()
5306     {
5307         //Roo.log(this.el.select('.navbar-toggle',true));
5308         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5309         
5310         var mark = {
5311             tag: "div",
5312             cls:"x-dlg-mask"
5313         };
5314         
5315         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5316         
5317         var size = this.el.getSize();
5318         this.maskEl.setSize(size.width, size.height);
5319         this.maskEl.enableDisplayMode("block");
5320         this.maskEl.hide();
5321         
5322         if(this.loadMask){
5323             this.maskEl.show();
5324         }
5325     },
5326     
5327     
5328     getChildContainer : function()
5329     {
5330         if (this.el && this.el.select('.collapse').getCount()) {
5331             return this.el.select('.collapse',true).first();
5332         }
5333         
5334         return this.el;
5335     },
5336     
5337     mask : function()
5338     {
5339         this.maskEl.show();
5340     },
5341     
5342     unmask : function()
5343     {
5344         this.maskEl.hide();
5345     },
5346     onToggle : function()
5347     {
5348         
5349         if(this.fireEvent('beforetoggle', this) === false){
5350             return;
5351         }
5352         var ce = this.el.select('.navbar-collapse',true).first();
5353       
5354         if (!ce.hasClass('show')) {
5355            this.expand();
5356         } else {
5357             this.collapse();
5358         }
5359         
5360         
5361     
5362     },
5363     /**
5364      * Expand the navbar pulldown 
5365      */
5366     expand : function ()
5367     {
5368        
5369         var ce = this.el.select('.navbar-collapse',true).first();
5370         if (ce.hasClass('collapsing')) {
5371             return;
5372         }
5373         ce.dom.style.height = '';
5374                // show it...
5375         ce.addClass('in'); // old...
5376         ce.removeClass('collapse');
5377         ce.addClass('show');
5378         var h = ce.getHeight();
5379         Roo.log(h);
5380         ce.removeClass('show');
5381         // at this point we should be able to see it..
5382         ce.addClass('collapsing');
5383         
5384         ce.setHeight(0); // resize it ...
5385         ce.on('transitionend', function() {
5386             //Roo.log('done transition');
5387             ce.removeClass('collapsing');
5388             ce.addClass('show');
5389             ce.removeClass('collapse');
5390
5391             ce.dom.style.height = '';
5392         }, this, { single: true} );
5393         ce.setHeight(h);
5394         ce.dom.scrollTop = 0;
5395     },
5396     /**
5397      * Collapse the navbar pulldown 
5398      */
5399     collapse : function()
5400     {
5401          var ce = this.el.select('.navbar-collapse',true).first();
5402        
5403         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5404             // it's collapsed or collapsing..
5405             return;
5406         }
5407         ce.removeClass('in'); // old...
5408         ce.setHeight(ce.getHeight());
5409         ce.removeClass('show');
5410         ce.addClass('collapsing');
5411         
5412         ce.on('transitionend', function() {
5413             ce.dom.style.height = '';
5414             ce.removeClass('collapsing');
5415             ce.addClass('collapse');
5416         }, this, { single: true} );
5417         ce.setHeight(0);
5418     }
5419     
5420     
5421     
5422 });
5423
5424
5425
5426  
5427
5428  /*
5429  * - LGPL
5430  *
5431  * navbar
5432  * 
5433  */
5434
5435 /**
5436  * @class Roo.bootstrap.NavSimplebar
5437  * @extends Roo.bootstrap.Navbar
5438  * Bootstrap Sidebar class
5439  *
5440  * @cfg {Boolean} inverse is inverted color
5441  * 
5442  * @cfg {String} type (nav | pills | tabs)
5443  * @cfg {Boolean} arrangement stacked | justified
5444  * @cfg {String} align (left | right) alignment
5445  * 
5446  * @cfg {Boolean} main (true|false) main nav bar? default false
5447  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5448  * 
5449  * @cfg {String} tag (header|footer|nav|div) default is nav 
5450
5451  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5452  * 
5453  * 
5454  * @constructor
5455  * Create a new Sidebar
5456  * @param {Object} config The config object
5457  */
5458
5459
5460 Roo.bootstrap.NavSimplebar = function(config){
5461     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5462 };
5463
5464 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5465     
5466     inverse: false,
5467     
5468     type: false,
5469     arrangement: '',
5470     align : false,
5471     
5472     weight : 'light',
5473     
5474     main : false,
5475     
5476     
5477     tag : false,
5478     
5479     
5480     getAutoCreate : function(){
5481         
5482         
5483         var cfg = {
5484             tag : this.tag || 'div',
5485             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5486         };
5487         if (['light','white'].indexOf(this.weight) > -1) {
5488             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5489         }
5490         cfg.cls += ' bg-' + this.weight;
5491         
5492         if (this.inverse) {
5493             cfg.cls += ' navbar-inverse';
5494             
5495         }
5496         
5497         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5498         
5499         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5500             return cfg;
5501         }
5502         
5503         
5504     
5505         
5506         cfg.cn = [
5507             {
5508                 cls: 'nav nav-' + this.xtype,
5509                 tag : 'ul'
5510             }
5511         ];
5512         
5513          
5514         this.type = this.type || 'nav';
5515         if (['tabs','pills'].indexOf(this.type) != -1) {
5516             cfg.cn[0].cls += ' nav-' + this.type
5517         
5518         
5519         } else {
5520             if (this.type!=='nav') {
5521                 Roo.log('nav type must be nav/tabs/pills')
5522             }
5523             cfg.cn[0].cls += ' navbar-nav'
5524         }
5525         
5526         
5527         
5528         
5529         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5530             cfg.cn[0].cls += ' nav-' + this.arrangement;
5531         }
5532         
5533         
5534         if (this.align === 'right') {
5535             cfg.cn[0].cls += ' navbar-right';
5536         }
5537         
5538         
5539         
5540         
5541         return cfg;
5542     
5543         
5544     }
5545     
5546     
5547     
5548 });
5549
5550
5551
5552  
5553
5554  
5555        /*
5556  * - LGPL
5557  *
5558  * navbar
5559  * navbar-fixed-top
5560  * navbar-expand-md  fixed-top 
5561  */
5562
5563 /**
5564  * @class Roo.bootstrap.NavHeaderbar
5565  * @extends Roo.bootstrap.NavSimplebar
5566  * Bootstrap Sidebar class
5567  *
5568  * @cfg {String} brand what is brand
5569  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5570  * @cfg {String} brand_href href of the brand
5571  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5572  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5573  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5574  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5575  * 
5576  * @constructor
5577  * Create a new Sidebar
5578  * @param {Object} config The config object
5579  */
5580
5581
5582 Roo.bootstrap.NavHeaderbar = function(config){
5583     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5584       
5585 };
5586
5587 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5588     
5589     position: '',
5590     brand: '',
5591     brand_href: false,
5592     srButton : true,
5593     autohide : false,
5594     desktopCenter : false,
5595    
5596     
5597     getAutoCreate : function(){
5598         
5599         var   cfg = {
5600             tag: this.nav || 'nav',
5601             cls: 'navbar navbar-expand-md',
5602             role: 'navigation',
5603             cn: []
5604         };
5605         
5606         var cn = cfg.cn;
5607         if (this.desktopCenter) {
5608             cn.push({cls : 'container', cn : []});
5609             cn = cn[0].cn;
5610         }
5611         
5612         if(this.srButton){
5613             var btn = {
5614                 tag: 'button',
5615                 type: 'button',
5616                 cls: 'navbar-toggle navbar-toggler',
5617                 'data-toggle': 'collapse',
5618                 cn: [
5619                     {
5620                         tag: 'span',
5621                         cls: 'sr-only',
5622                         html: 'Toggle navigation'
5623                     },
5624                     {
5625                         tag: 'span',
5626                         cls: 'icon-bar navbar-toggler-icon'
5627                     },
5628                     {
5629                         tag: 'span',
5630                         cls: 'icon-bar'
5631                     },
5632                     {
5633                         tag: 'span',
5634                         cls: 'icon-bar'
5635                     }
5636                 ]
5637             };
5638             
5639             cn.push( Roo.bootstrap.version == 4 ? btn : {
5640                 tag: 'div',
5641                 cls: 'navbar-header',
5642                 cn: [
5643                     btn
5644                 ]
5645             });
5646         }
5647         
5648         cn.push({
5649             tag: 'div',
5650             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5651             cn : []
5652         });
5653         
5654         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5655         
5656         if (['light','white'].indexOf(this.weight) > -1) {
5657             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5658         }
5659         cfg.cls += ' bg-' + this.weight;
5660         
5661         
5662         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5663             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5664             
5665             // tag can override this..
5666             
5667             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5668         }
5669         
5670         if (this.brand !== '') {
5671             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5672             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5673                 tag: 'a',
5674                 href: this.brand_href ? this.brand_href : '#',
5675                 cls: 'navbar-brand',
5676                 cn: [
5677                 this.brand
5678                 ]
5679             });
5680         }
5681         
5682         if(this.main){
5683             cfg.cls += ' main-nav';
5684         }
5685         
5686         
5687         return cfg;
5688
5689         
5690     },
5691     getHeaderChildContainer : function()
5692     {
5693         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5694             return this.el.select('.navbar-header',true).first();
5695         }
5696         
5697         return this.getChildContainer();
5698     },
5699     
5700     getChildContainer : function()
5701     {
5702          
5703         return this.el.select('.roo-navbar-collapse',true).first();
5704          
5705         
5706     },
5707     
5708     initEvents : function()
5709     {
5710         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5711         
5712         if (this.autohide) {
5713             
5714             var prevScroll = 0;
5715             var ft = this.el;
5716             
5717             Roo.get(document).on('scroll',function(e) {
5718                 var ns = Roo.get(document).getScroll().top;
5719                 var os = prevScroll;
5720                 prevScroll = ns;
5721                 
5722                 if(ns > os){
5723                     ft.removeClass('slideDown');
5724                     ft.addClass('slideUp');
5725                     return;
5726                 }
5727                 ft.removeClass('slideUp');
5728                 ft.addClass('slideDown');
5729                  
5730               
5731           },this);
5732         }
5733     }    
5734     
5735 });
5736
5737
5738
5739  
5740
5741  /*
5742  * - LGPL
5743  *
5744  * navbar
5745  * 
5746  */
5747
5748 /**
5749  * @class Roo.bootstrap.NavSidebar
5750  * @extends Roo.bootstrap.Navbar
5751  * Bootstrap Sidebar class
5752  * 
5753  * @constructor
5754  * Create a new Sidebar
5755  * @param {Object} config The config object
5756  */
5757
5758
5759 Roo.bootstrap.NavSidebar = function(config){
5760     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5761 };
5762
5763 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5764     
5765     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5766     
5767     getAutoCreate : function(){
5768         
5769         
5770         return  {
5771             tag: 'div',
5772             cls: 'sidebar sidebar-nav'
5773         };
5774     
5775         
5776     }
5777     
5778     
5779     
5780 });
5781
5782
5783
5784  
5785
5786  /*
5787  * - LGPL
5788  *
5789  * nav group
5790  * 
5791  */
5792
5793 /**
5794  * @class Roo.bootstrap.NavGroup
5795  * @extends Roo.bootstrap.Component
5796  * Bootstrap NavGroup class
5797  * @cfg {String} align (left|right)
5798  * @cfg {Boolean} inverse
5799  * @cfg {String} type (nav|pills|tab) default nav
5800  * @cfg {String} navId - reference Id for navbar.
5801  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5802  * 
5803  * @constructor
5804  * Create a new nav group
5805  * @param {Object} config The config object
5806  */
5807
5808 Roo.bootstrap.NavGroup = function(config){
5809     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5810     this.navItems = [];
5811    
5812     Roo.bootstrap.NavGroup.register(this);
5813      this.addEvents({
5814         /**
5815              * @event changed
5816              * Fires when the active item changes
5817              * @param {Roo.bootstrap.NavGroup} this
5818              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5819              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5820          */
5821         'changed': true
5822      });
5823     
5824 };
5825
5826 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5827     
5828     align: '',
5829     inverse: false,
5830     form: false,
5831     type: 'nav',
5832     navId : '',
5833     // private
5834     pilltype : true,
5835     
5836     navItems : false, 
5837     
5838     getAutoCreate : function()
5839     {
5840         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5841         
5842         cfg = {
5843             tag : 'ul',
5844             cls: 'nav' 
5845         };
5846         if (Roo.bootstrap.version == 4) {
5847             if (['tabs','pills'].indexOf(this.type) != -1) {
5848                 cfg.cls += ' nav-' + this.type; 
5849             } else {
5850                 // trying to remove so header bar can right align top?
5851                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5852                     // do not use on header bar... 
5853                     cfg.cls += ' navbar-nav';
5854                 }
5855             }
5856             
5857         } else {
5858             if (['tabs','pills'].indexOf(this.type) != -1) {
5859                 cfg.cls += ' nav-' + this.type
5860             } else {
5861                 if (this.type !== 'nav') {
5862                     Roo.log('nav type must be nav/tabs/pills')
5863                 }
5864                 cfg.cls += ' navbar-nav'
5865             }
5866         }
5867         
5868         if (this.parent() && this.parent().sidebar) {
5869             cfg = {
5870                 tag: 'ul',
5871                 cls: 'dashboard-menu sidebar-menu'
5872             };
5873             
5874             return cfg;
5875         }
5876         
5877         if (this.form === true) {
5878             cfg = {
5879                 tag: 'form',
5880                 cls: 'navbar-form form-inline'
5881             };
5882             //nav navbar-right ml-md-auto
5883             if (this.align === 'right') {
5884                 cfg.cls += ' navbar-right ml-md-auto';
5885             } else {
5886                 cfg.cls += ' navbar-left';
5887             }
5888         }
5889         
5890         if (this.align === 'right') {
5891             cfg.cls += ' navbar-right ml-md-auto';
5892         } else {
5893             cfg.cls += ' mr-auto';
5894         }
5895         
5896         if (this.inverse) {
5897             cfg.cls += ' navbar-inverse';
5898             
5899         }
5900         
5901         
5902         return cfg;
5903     },
5904     /**
5905     * sets the active Navigation item
5906     * @param {Roo.bootstrap.NavItem} the new current navitem
5907     */
5908     setActiveItem : function(item)
5909     {
5910         var prev = false;
5911         Roo.each(this.navItems, function(v){
5912             if (v == item) {
5913                 return ;
5914             }
5915             if (v.isActive()) {
5916                 v.setActive(false, true);
5917                 prev = v;
5918                 
5919             }
5920             
5921         });
5922
5923         item.setActive(true, true);
5924         this.fireEvent('changed', this, item, prev);
5925         
5926         
5927     },
5928     /**
5929     * gets the active Navigation item
5930     * @return {Roo.bootstrap.NavItem} the current navitem
5931     */
5932     getActive : function()
5933     {
5934         
5935         var prev = false;
5936         Roo.each(this.navItems, function(v){
5937             
5938             if (v.isActive()) {
5939                 prev = v;
5940                 
5941             }
5942             
5943         });
5944         return prev;
5945     },
5946     
5947     indexOfNav : function()
5948     {
5949         
5950         var prev = false;
5951         Roo.each(this.navItems, function(v,i){
5952             
5953             if (v.isActive()) {
5954                 prev = i;
5955                 
5956             }
5957             
5958         });
5959         return prev;
5960     },
5961     /**
5962     * adds a Navigation item
5963     * @param {Roo.bootstrap.NavItem} the navitem to add
5964     */
5965     addItem : function(cfg)
5966     {
5967         if (this.form && Roo.bootstrap.version == 4) {
5968             cfg.tag = 'div';
5969         }
5970         var cn = new Roo.bootstrap.NavItem(cfg);
5971         this.register(cn);
5972         cn.parentId = this.id;
5973         cn.onRender(this.el, null);
5974         return cn;
5975     },
5976     /**
5977     * register a Navigation item
5978     * @param {Roo.bootstrap.NavItem} the navitem to add
5979     */
5980     register : function(item)
5981     {
5982         this.navItems.push( item);
5983         item.navId = this.navId;
5984     
5985     },
5986     
5987     /**
5988     * clear all the Navigation item
5989     */
5990    
5991     clearAll : function()
5992     {
5993         this.navItems = [];
5994         this.el.dom.innerHTML = '';
5995     },
5996     
5997     getNavItem: function(tabId)
5998     {
5999         var ret = false;
6000         Roo.each(this.navItems, function(e) {
6001             if (e.tabId == tabId) {
6002                ret =  e;
6003                return false;
6004             }
6005             return true;
6006             
6007         });
6008         return ret;
6009     },
6010     
6011     setActiveNext : function()
6012     {
6013         var i = this.indexOfNav(this.getActive());
6014         if (i > this.navItems.length) {
6015             return;
6016         }
6017         this.setActiveItem(this.navItems[i+1]);
6018     },
6019     setActivePrev : function()
6020     {
6021         var i = this.indexOfNav(this.getActive());
6022         if (i  < 1) {
6023             return;
6024         }
6025         this.setActiveItem(this.navItems[i-1]);
6026     },
6027     clearWasActive : function(except) {
6028         Roo.each(this.navItems, function(e) {
6029             if (e.tabId != except.tabId && e.was_active) {
6030                e.was_active = false;
6031                return false;
6032             }
6033             return true;
6034             
6035         });
6036     },
6037     getWasActive : function ()
6038     {
6039         var r = false;
6040         Roo.each(this.navItems, function(e) {
6041             if (e.was_active) {
6042                r = e;
6043                return false;
6044             }
6045             return true;
6046             
6047         });
6048         return r;
6049     }
6050     
6051     
6052 });
6053
6054  
6055 Roo.apply(Roo.bootstrap.NavGroup, {
6056     
6057     groups: {},
6058      /**
6059     * register a Navigation Group
6060     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6061     */
6062     register : function(navgrp)
6063     {
6064         this.groups[navgrp.navId] = navgrp;
6065         
6066     },
6067     /**
6068     * fetch a Navigation Group based on the navigation ID
6069     * @param {string} the navgroup to add
6070     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6071     */
6072     get: function(navId) {
6073         if (typeof(this.groups[navId]) == 'undefined') {
6074             return false;
6075             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6076         }
6077         return this.groups[navId] ;
6078     }
6079     
6080     
6081     
6082 });
6083
6084  /*
6085  * - LGPL
6086  *
6087  * row
6088  * 
6089  */
6090
6091 /**
6092  * @class Roo.bootstrap.NavItem
6093  * @extends Roo.bootstrap.Component
6094  * Bootstrap Navbar.NavItem class
6095  * @cfg {String} href  link to
6096  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6097  * @cfg {Boolean} button_outline show and outlined button
6098  * @cfg {String} html content of button
6099  * @cfg {String} badge text inside badge
6100  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6101  * @cfg {String} glyphicon DEPRICATED - use fa
6102  * @cfg {String} icon DEPRICATED - use fa
6103  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6104  * @cfg {Boolean} active Is item active
6105  * @cfg {Boolean} disabled Is item disabled
6106  * @cfg {String} linkcls  Link Class
6107  * @cfg {Boolean} preventDefault (true | false) default false
6108  * @cfg {String} tabId the tab that this item activates.
6109  * @cfg {String} tagtype (a|span) render as a href or span?
6110  * @cfg {Boolean} animateRef (true|false) link to element default false  
6111   
6112  * @constructor
6113  * Create a new Navbar Item
6114  * @param {Object} config The config object
6115  */
6116 Roo.bootstrap.NavItem = function(config){
6117     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6118     this.addEvents({
6119         // raw events
6120         /**
6121          * @event click
6122          * The raw click event for the entire grid.
6123          * @param {Roo.EventObject} e
6124          */
6125         "click" : true,
6126          /**
6127             * @event changed
6128             * Fires when the active item active state changes
6129             * @param {Roo.bootstrap.NavItem} this
6130             * @param {boolean} state the new state
6131              
6132          */
6133         'changed': true,
6134         /**
6135             * @event scrollto
6136             * Fires when scroll to element
6137             * @param {Roo.bootstrap.NavItem} this
6138             * @param {Object} options
6139             * @param {Roo.EventObject} e
6140              
6141          */
6142         'scrollto': true
6143     });
6144    
6145 };
6146
6147 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6148     
6149     href: false,
6150     html: '',
6151     badge: '',
6152     icon: false,
6153     fa : false,
6154     glyphicon: false,
6155     active: false,
6156     preventDefault : false,
6157     tabId : false,
6158     tagtype : 'a',
6159     tag: 'li',
6160     disabled : false,
6161     animateRef : false,
6162     was_active : false,
6163     button_weight : '',
6164     button_outline : false,
6165     linkcls : '',
6166     navLink: false,
6167     
6168     getAutoCreate : function(){
6169          
6170         var cfg = {
6171             tag: this.tag,
6172             cls: 'nav-item'
6173         };
6174         
6175         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6176         
6177         if (this.active) {
6178             cfg.cls +=  ' active' ;
6179         }
6180         if (this.disabled) {
6181             cfg.cls += ' disabled';
6182         }
6183         
6184         // BS4 only?
6185         if (this.button_weight.length) {
6186             cfg.tag = this.href ? 'a' : 'button';
6187             cfg.html = this.html || '';
6188             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6189             if (this.href) {
6190                 cfg.href = this.href;
6191             }
6192             if (this.fa) {
6193                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6194             } else {
6195                 cfg.cls += " nav-html";
6196             }
6197             
6198             // menu .. should add dropdown-menu class - so no need for carat..
6199             
6200             if (this.badge !== '') {
6201                  
6202                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6203             }
6204             return cfg;
6205         }
6206         
6207         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6208             cfg.cn = [
6209                 {
6210                     tag: this.tagtype,
6211                     href : this.href || "#",
6212                     html: this.html || '',
6213                     cls : ''
6214                 }
6215             ];
6216             if (this.tagtype == 'a') {
6217                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6218         
6219             }
6220             if (this.icon) {
6221                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6222             } else  if (this.fa) {
6223                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6224             } else if(this.glyphicon) {
6225                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6226             } else {
6227                 cfg.cn[0].cls += " nav-html";
6228             }
6229             
6230             if (this.menu) {
6231                 cfg.cn[0].html += " <span class='caret'></span>";
6232              
6233             }
6234             
6235             if (this.badge !== '') {
6236                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6237             }
6238         }
6239         
6240         
6241         
6242         return cfg;
6243     },
6244     onRender : function(ct, position)
6245     {
6246        // Roo.log("Call onRender: " + this.xtype);
6247         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6248             this.tag = 'div';
6249         }
6250         
6251         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6252         this.navLink = this.el.select('.nav-link',true).first();
6253         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6254         return ret;
6255     },
6256       
6257     
6258     initEvents: function() 
6259     {
6260         if (typeof (this.menu) != 'undefined') {
6261             this.menu.parentType = this.xtype;
6262             this.menu.triggerEl = this.el;
6263             this.menu = this.addxtype(Roo.apply({}, this.menu));
6264         }
6265         
6266         this.el.on('click', this.onClick, this);
6267         
6268         //if(this.tagtype == 'span'){
6269         //    this.el.select('span',true).on('click', this.onClick, this);
6270         //}
6271        
6272         // at this point parent should be available..
6273         this.parent().register(this);
6274     },
6275     
6276     onClick : function(e)
6277     {
6278         if (e.getTarget('.dropdown-menu-item')) {
6279             // did you click on a menu itemm.... - then don't trigger onclick..
6280             return;
6281         }
6282         
6283         if(
6284                 this.preventDefault || 
6285                 this.href == '#' 
6286         ){
6287             Roo.log("NavItem - prevent Default?");
6288             e.preventDefault();
6289         }
6290         
6291         if (this.disabled) {
6292             return;
6293         }
6294         
6295         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6296         if (tg && tg.transition) {
6297             Roo.log("waiting for the transitionend");
6298             return;
6299         }
6300         
6301         
6302         
6303         //Roo.log("fire event clicked");
6304         if(this.fireEvent('click', this, e) === false){
6305             return;
6306         };
6307         
6308         if(this.tagtype == 'span'){
6309             return;
6310         }
6311         
6312         //Roo.log(this.href);
6313         var ael = this.el.select('a',true).first();
6314         //Roo.log(ael);
6315         
6316         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6317             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6318             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6319                 return; // ignore... - it's a 'hash' to another page.
6320             }
6321             Roo.log("NavItem - prevent Default?");
6322             e.preventDefault();
6323             this.scrollToElement(e);
6324         }
6325         
6326         
6327         var p =  this.parent();
6328    
6329         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6330             if (typeof(p.setActiveItem) !== 'undefined') {
6331                 p.setActiveItem(this);
6332             }
6333         }
6334         
6335         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6336         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6337             // remove the collapsed menu expand...
6338             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6339         }
6340     },
6341     
6342     isActive: function () {
6343         return this.active
6344     },
6345     setActive : function(state, fire, is_was_active)
6346     {
6347         if (this.active && !state && this.navId) {
6348             this.was_active = true;
6349             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6350             if (nv) {
6351                 nv.clearWasActive(this);
6352             }
6353             
6354         }
6355         this.active = state;
6356         
6357         if (!state ) {
6358             this.el.removeClass('active');
6359             this.navLink ? this.navLink.removeClass('active') : false;
6360         } else if (!this.el.hasClass('active')) {
6361             
6362             this.el.addClass('active');
6363             if (Roo.bootstrap.version == 4 && this.navLink ) {
6364                 this.navLink.addClass('active');
6365             }
6366             
6367         }
6368         if (fire) {
6369             this.fireEvent('changed', this, state);
6370         }
6371         
6372         // show a panel if it's registered and related..
6373         
6374         if (!this.navId || !this.tabId || !state || is_was_active) {
6375             return;
6376         }
6377         
6378         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6379         if (!tg) {
6380             return;
6381         }
6382         var pan = tg.getPanelByName(this.tabId);
6383         if (!pan) {
6384             return;
6385         }
6386         // if we can not flip to new panel - go back to old nav highlight..
6387         if (false == tg.showPanel(pan)) {
6388             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6389             if (nv) {
6390                 var onav = nv.getWasActive();
6391                 if (onav) {
6392                     onav.setActive(true, false, true);
6393                 }
6394             }
6395             
6396         }
6397         
6398         
6399         
6400     },
6401      // this should not be here...
6402     setDisabled : function(state)
6403     {
6404         this.disabled = state;
6405         if (!state ) {
6406             this.el.removeClass('disabled');
6407         } else if (!this.el.hasClass('disabled')) {
6408             this.el.addClass('disabled');
6409         }
6410         
6411     },
6412     
6413     /**
6414      * Fetch the element to display the tooltip on.
6415      * @return {Roo.Element} defaults to this.el
6416      */
6417     tooltipEl : function()
6418     {
6419         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6420     },
6421     
6422     scrollToElement : function(e)
6423     {
6424         var c = document.body;
6425         
6426         /*
6427          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6428          */
6429         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6430             c = document.documentElement;
6431         }
6432         
6433         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6434         
6435         if(!target){
6436             return;
6437         }
6438
6439         var o = target.calcOffsetsTo(c);
6440         
6441         var options = {
6442             target : target,
6443             value : o[1]
6444         };
6445         
6446         this.fireEvent('scrollto', this, options, e);
6447         
6448         Roo.get(c).scrollTo('top', options.value, true);
6449         
6450         return;
6451     },
6452     /**
6453      * Set the HTML (text content) of the item
6454      * @param {string} html  content for the nav item
6455      */
6456     setHtml : function(html)
6457     {
6458         this.html = html;
6459         this.htmlEl.dom.innerHTML = html;
6460         
6461     } 
6462 });
6463  
6464
6465  /*
6466  * - LGPL
6467  *
6468  * sidebar item
6469  *
6470  *  li
6471  *    <span> icon </span>
6472  *    <span> text </span>
6473  *    <span>badge </span>
6474  */
6475
6476 /**
6477  * @class Roo.bootstrap.NavSidebarItem
6478  * @extends Roo.bootstrap.NavItem
6479  * Bootstrap Navbar.NavSidebarItem class
6480  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6481  * {Boolean} open is the menu open
6482  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6483  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6484  * {String} buttonSize (sm|md|lg)the extra classes for the button
6485  * {Boolean} showArrow show arrow next to the text (default true)
6486  * @constructor
6487  * Create a new Navbar Button
6488  * @param {Object} config The config object
6489  */
6490 Roo.bootstrap.NavSidebarItem = function(config){
6491     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6492     this.addEvents({
6493         // raw events
6494         /**
6495          * @event click
6496          * The raw click event for the entire grid.
6497          * @param {Roo.EventObject} e
6498          */
6499         "click" : true,
6500          /**
6501             * @event changed
6502             * Fires when the active item active state changes
6503             * @param {Roo.bootstrap.NavSidebarItem} this
6504             * @param {boolean} state the new state
6505              
6506          */
6507         'changed': true
6508     });
6509    
6510 };
6511
6512 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6513     
6514     badgeWeight : 'default',
6515     
6516     open: false,
6517     
6518     buttonView : false,
6519     
6520     buttonWeight : 'default',
6521     
6522     buttonSize : 'md',
6523     
6524     showArrow : true,
6525     
6526     getAutoCreate : function(){
6527         
6528         
6529         var a = {
6530                 tag: 'a',
6531                 href : this.href || '#',
6532                 cls: '',
6533                 html : '',
6534                 cn : []
6535         };
6536         
6537         if(this.buttonView){
6538             a = {
6539                 tag: 'button',
6540                 href : this.href || '#',
6541                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6542                 html : this.html,
6543                 cn : []
6544             };
6545         }
6546         
6547         var cfg = {
6548             tag: 'li',
6549             cls: '',
6550             cn: [ a ]
6551         };
6552         
6553         if (this.active) {
6554             cfg.cls += ' active';
6555         }
6556         
6557         if (this.disabled) {
6558             cfg.cls += ' disabled';
6559         }
6560         if (this.open) {
6561             cfg.cls += ' open x-open';
6562         }
6563         // left icon..
6564         if (this.glyphicon || this.icon) {
6565             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6566             a.cn.push({ tag : 'i', cls : c }) ;
6567         }
6568         
6569         if(!this.buttonView){
6570             var span = {
6571                 tag: 'span',
6572                 html : this.html || ''
6573             };
6574
6575             a.cn.push(span);
6576             
6577         }
6578         
6579         if (this.badge !== '') {
6580             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6581         }
6582         
6583         if (this.menu) {
6584             
6585             if(this.showArrow){
6586                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6587             }
6588             
6589             a.cls += ' dropdown-toggle treeview' ;
6590         }
6591         
6592         return cfg;
6593     },
6594     
6595     initEvents : function()
6596     { 
6597         if (typeof (this.menu) != 'undefined') {
6598             this.menu.parentType = this.xtype;
6599             this.menu.triggerEl = this.el;
6600             this.menu = this.addxtype(Roo.apply({}, this.menu));
6601         }
6602         
6603         this.el.on('click', this.onClick, this);
6604         
6605         if(this.badge !== ''){
6606             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6607         }
6608         
6609     },
6610     
6611     onClick : function(e)
6612     {
6613         if(this.disabled){
6614             e.preventDefault();
6615             return;
6616         }
6617         
6618         if(this.preventDefault){
6619             e.preventDefault();
6620         }
6621         
6622         this.fireEvent('click', this, e);
6623     },
6624     
6625     disable : function()
6626     {
6627         this.setDisabled(true);
6628     },
6629     
6630     enable : function()
6631     {
6632         this.setDisabled(false);
6633     },
6634     
6635     setDisabled : function(state)
6636     {
6637         if(this.disabled == state){
6638             return;
6639         }
6640         
6641         this.disabled = state;
6642         
6643         if (state) {
6644             this.el.addClass('disabled');
6645             return;
6646         }
6647         
6648         this.el.removeClass('disabled');
6649         
6650         return;
6651     },
6652     
6653     setActive : function(state)
6654     {
6655         if(this.active == state){
6656             return;
6657         }
6658         
6659         this.active = state;
6660         
6661         if (state) {
6662             this.el.addClass('active');
6663             return;
6664         }
6665         
6666         this.el.removeClass('active');
6667         
6668         return;
6669     },
6670     
6671     isActive: function () 
6672     {
6673         return this.active;
6674     },
6675     
6676     setBadge : function(str)
6677     {
6678         if(!this.badgeEl){
6679             return;
6680         }
6681         
6682         this.badgeEl.dom.innerHTML = str;
6683     }
6684     
6685    
6686      
6687  
6688 });
6689  
6690
6691  /*
6692  * - LGPL
6693  *
6694  *  Breadcrumb Nav
6695  * 
6696  */
6697 Roo.namespace('Roo.bootstrap.breadcrumb');
6698
6699
6700 /**
6701  * @class Roo.bootstrap.breadcrumb.Nav
6702  * @extends Roo.bootstrap.Component
6703  * Bootstrap Breadcrumb Nav Class
6704  *  
6705  * @children Roo.bootstrap.breadcrumb.Item
6706  * 
6707  * @constructor
6708  * Create a new breadcrumb.Nav
6709  * @param {Object} config The config object
6710  */
6711
6712
6713 Roo.bootstrap.breadcrumb.Nav = function(config){
6714     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6715     
6716     
6717 };
6718
6719 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6720     
6721     getAutoCreate : function()
6722     {
6723
6724         var cfg = {
6725             tag: 'nav',
6726             cn : [
6727                 {
6728                     tag : 'ol',
6729                     cls : 'breadcrumb'
6730                 }
6731             ]
6732             
6733         };
6734           
6735         return cfg;
6736     },
6737     
6738     initEvents: function()
6739     {
6740         this.olEl = this.el.select('ol',true).first();    
6741     },
6742     getChildContainer : function()
6743     {
6744         return this.olEl;  
6745     }
6746     
6747 });
6748
6749  /*
6750  * - LGPL
6751  *
6752  *  Breadcrumb Item
6753  * 
6754  */
6755
6756
6757 /**
6758  * @class Roo.bootstrap.breadcrumb.Nav
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Breadcrumb Nav Class
6761  *  
6762  * @children Roo.bootstrap.breadcrumb.Component
6763  * @cfg {String} html the content of the link.
6764  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6765  * @cfg {Boolean} active is it active
6766
6767  * 
6768  * @constructor
6769  * Create a new breadcrumb.Nav
6770  * @param {Object} config The config object
6771  */
6772
6773 Roo.bootstrap.breadcrumb.Item = function(config){
6774     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6775     this.addEvents({
6776         // img events
6777         /**
6778          * @event click
6779          * The img click event for the img.
6780          * @param {Roo.EventObject} e
6781          */
6782         "click" : true
6783     });
6784     
6785 };
6786
6787 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6788     
6789     href: false,
6790     html : '',
6791     
6792     getAutoCreate : function()
6793     {
6794
6795         var cfg = {
6796             tag: 'li',
6797             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6798         };
6799         if (this.href !== false) {
6800             cfg.cn = [{
6801                 tag : 'a',
6802                 href : this.href,
6803                 html : this.html
6804             }];
6805         } else {
6806             cfg.html = this.html;
6807         }
6808         
6809         return cfg;
6810     },
6811     
6812     initEvents: function()
6813     {
6814         if (this.href) {
6815             this.el.select('a', true).first().on('click',this.onClick, this)
6816         }
6817         
6818     },
6819     onClick : function(e)
6820     {
6821         e.preventDefault();
6822         this.fireEvent('click',this,  e);
6823     }
6824     
6825 });
6826
6827  /*
6828  * - LGPL
6829  *
6830  * row
6831  * 
6832  */
6833
6834 /**
6835  * @class Roo.bootstrap.Row
6836  * @extends Roo.bootstrap.Component
6837  * Bootstrap Row class (contains columns...)
6838  * 
6839  * @constructor
6840  * Create a new Row
6841  * @param {Object} config The config object
6842  */
6843
6844 Roo.bootstrap.Row = function(config){
6845     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6846 };
6847
6848 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6849     
6850     getAutoCreate : function(){
6851        return {
6852             cls: 'row clearfix'
6853        };
6854     }
6855     
6856     
6857 });
6858
6859  
6860
6861  /*
6862  * - LGPL
6863  *
6864  * pagination
6865  * 
6866  */
6867
6868 /**
6869  * @class Roo.bootstrap.Pagination
6870  * @extends Roo.bootstrap.Component
6871  * Bootstrap Pagination class
6872  * @cfg {String} size xs | sm | md | lg
6873  * @cfg {Boolean} inverse false | true
6874  * 
6875  * @constructor
6876  * Create a new Pagination
6877  * @param {Object} config The config object
6878  */
6879
6880 Roo.bootstrap.Pagination = function(config){
6881     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6882 };
6883
6884 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6885     
6886     cls: false,
6887     size: false,
6888     inverse: false,
6889     
6890     getAutoCreate : function(){
6891         var cfg = {
6892             tag: 'ul',
6893                 cls: 'pagination'
6894         };
6895         if (this.inverse) {
6896             cfg.cls += ' inverse';
6897         }
6898         if (this.html) {
6899             cfg.html=this.html;
6900         }
6901         if (this.cls) {
6902             cfg.cls += " " + this.cls;
6903         }
6904         return cfg;
6905     }
6906    
6907 });
6908
6909  
6910
6911  /*
6912  * - LGPL
6913  *
6914  * Pagination item
6915  * 
6916  */
6917
6918
6919 /**
6920  * @class Roo.bootstrap.PaginationItem
6921  * @extends Roo.bootstrap.Component
6922  * Bootstrap PaginationItem class
6923  * @cfg {String} html text
6924  * @cfg {String} href the link
6925  * @cfg {Boolean} preventDefault (true | false) default true
6926  * @cfg {Boolean} active (true | false) default false
6927  * @cfg {Boolean} disabled default false
6928  * 
6929  * 
6930  * @constructor
6931  * Create a new PaginationItem
6932  * @param {Object} config The config object
6933  */
6934
6935
6936 Roo.bootstrap.PaginationItem = function(config){
6937     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6938     this.addEvents({
6939         // raw events
6940         /**
6941          * @event click
6942          * The raw click event for the entire grid.
6943          * @param {Roo.EventObject} e
6944          */
6945         "click" : true
6946     });
6947 };
6948
6949 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6950     
6951     href : false,
6952     html : false,
6953     preventDefault: true,
6954     active : false,
6955     cls : false,
6956     disabled: false,
6957     
6958     getAutoCreate : function(){
6959         var cfg= {
6960             tag: 'li',
6961             cn: [
6962                 {
6963                     tag : 'a',
6964                     href : this.href ? this.href : '#',
6965                     html : this.html ? this.html : ''
6966                 }
6967             ]
6968         };
6969         
6970         if(this.cls){
6971             cfg.cls = this.cls;
6972         }
6973         
6974         if(this.disabled){
6975             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6976         }
6977         
6978         if(this.active){
6979             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6980         }
6981         
6982         return cfg;
6983     },
6984     
6985     initEvents: function() {
6986         
6987         this.el.on('click', this.onClick, this);
6988         
6989     },
6990     onClick : function(e)
6991     {
6992         Roo.log('PaginationItem on click ');
6993         if(this.preventDefault){
6994             e.preventDefault();
6995         }
6996         
6997         if(this.disabled){
6998             return;
6999         }
7000         
7001         this.fireEvent('click', this, e);
7002     }
7003    
7004 });
7005
7006  
7007
7008  /*
7009  * - LGPL
7010  *
7011  * slider
7012  * 
7013  */
7014
7015
7016 /**
7017  * @class Roo.bootstrap.Slider
7018  * @extends Roo.bootstrap.Component
7019  * Bootstrap Slider class
7020  *    
7021  * @constructor
7022  * Create a new Slider
7023  * @param {Object} config The config object
7024  */
7025
7026 Roo.bootstrap.Slider = function(config){
7027     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7028 };
7029
7030 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7031     
7032     getAutoCreate : function(){
7033         
7034         var cfg = {
7035             tag: 'div',
7036             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7037             cn: [
7038                 {
7039                     tag: 'a',
7040                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7041                 }
7042             ]
7043         };
7044         
7045         return cfg;
7046     }
7047    
7048 });
7049
7050  /*
7051  * Based on:
7052  * Ext JS Library 1.1.1
7053  * Copyright(c) 2006-2007, Ext JS, LLC.
7054  *
7055  * Originally Released Under LGPL - original licence link has changed is not relivant.
7056  *
7057  * Fork - LGPL
7058  * <script type="text/javascript">
7059  */
7060  
7061
7062 /**
7063  * @class Roo.grid.ColumnModel
7064  * @extends Roo.util.Observable
7065  * This is the default implementation of a ColumnModel used by the Grid. It defines
7066  * the columns in the grid.
7067  * <br>Usage:<br>
7068  <pre><code>
7069  var colModel = new Roo.grid.ColumnModel([
7070         {header: "Ticker", width: 60, sortable: true, locked: true},
7071         {header: "Company Name", width: 150, sortable: true},
7072         {header: "Market Cap.", width: 100, sortable: true},
7073         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7074         {header: "Employees", width: 100, sortable: true, resizable: false}
7075  ]);
7076  </code></pre>
7077  * <p>
7078  
7079  * The config options listed for this class are options which may appear in each
7080  * individual column definition.
7081  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7082  * @constructor
7083  * @param {Object} config An Array of column config objects. See this class's
7084  * config objects for details.
7085 */
7086 Roo.grid.ColumnModel = function(config){
7087         /**
7088      * The config passed into the constructor
7089      */
7090     this.config = config;
7091     this.lookup = {};
7092
7093     // if no id, create one
7094     // if the column does not have a dataIndex mapping,
7095     // map it to the order it is in the config
7096     for(var i = 0, len = config.length; i < len; i++){
7097         var c = config[i];
7098         if(typeof c.dataIndex == "undefined"){
7099             c.dataIndex = i;
7100         }
7101         if(typeof c.renderer == "string"){
7102             c.renderer = Roo.util.Format[c.renderer];
7103         }
7104         if(typeof c.id == "undefined"){
7105             c.id = Roo.id();
7106         }
7107         if(c.editor && c.editor.xtype){
7108             c.editor  = Roo.factory(c.editor, Roo.grid);
7109         }
7110         if(c.editor && c.editor.isFormField){
7111             c.editor = new Roo.grid.GridEditor(c.editor);
7112         }
7113         this.lookup[c.id] = c;
7114     }
7115
7116     /**
7117      * The width of columns which have no width specified (defaults to 100)
7118      * @type Number
7119      */
7120     this.defaultWidth = 100;
7121
7122     /**
7123      * Default sortable of columns which have no sortable specified (defaults to false)
7124      * @type Boolean
7125      */
7126     this.defaultSortable = false;
7127
7128     this.addEvents({
7129         /**
7130              * @event widthchange
7131              * Fires when the width of a column changes.
7132              * @param {ColumnModel} this
7133              * @param {Number} columnIndex The column index
7134              * @param {Number} newWidth The new width
7135              */
7136             "widthchange": true,
7137         /**
7138              * @event headerchange
7139              * Fires when the text of a header changes.
7140              * @param {ColumnModel} this
7141              * @param {Number} columnIndex The column index
7142              * @param {Number} newText The new header text
7143              */
7144             "headerchange": true,
7145         /**
7146              * @event hiddenchange
7147              * Fires when a column is hidden or "unhidden".
7148              * @param {ColumnModel} this
7149              * @param {Number} columnIndex The column index
7150              * @param {Boolean} hidden true if hidden, false otherwise
7151              */
7152             "hiddenchange": true,
7153             /**
7154          * @event columnmoved
7155          * Fires when a column is moved.
7156          * @param {ColumnModel} this
7157          * @param {Number} oldIndex
7158          * @param {Number} newIndex
7159          */
7160         "columnmoved" : true,
7161         /**
7162          * @event columlockchange
7163          * Fires when a column's locked state is changed
7164          * @param {ColumnModel} this
7165          * @param {Number} colIndex
7166          * @param {Boolean} locked true if locked
7167          */
7168         "columnlockchange" : true
7169     });
7170     Roo.grid.ColumnModel.superclass.constructor.call(this);
7171 };
7172 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7173     /**
7174      * @cfg {String} header The header text to display in the Grid view.
7175      */
7176     /**
7177      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7178      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7179      * specified, the column's index is used as an index into the Record's data Array.
7180      */
7181     /**
7182      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7183      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7184      */
7185     /**
7186      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7187      * Defaults to the value of the {@link #defaultSortable} property.
7188      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7189      */
7190     /**
7191      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7192      */
7193     /**
7194      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7195      */
7196     /**
7197      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7198      */
7199     /**
7200      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7201      */
7202     /**
7203      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7204      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7205      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7206      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7207      */
7208        /**
7209      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7210      */
7211     /**
7212      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7213      */
7214     /**
7215      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7216      */
7217     /**
7218      * @cfg {String} cursor (Optional)
7219      */
7220     /**
7221      * @cfg {String} tooltip (Optional)
7222      */
7223     /**
7224      * @cfg {Number} xs (Optional)
7225      */
7226     /**
7227      * @cfg {Number} sm (Optional)
7228      */
7229     /**
7230      * @cfg {Number} md (Optional)
7231      */
7232     /**
7233      * @cfg {Number} lg (Optional)
7234      */
7235     /**
7236      * Returns the id of the column at the specified index.
7237      * @param {Number} index The column index
7238      * @return {String} the id
7239      */
7240     getColumnId : function(index){
7241         return this.config[index].id;
7242     },
7243
7244     /**
7245      * Returns the column for a specified id.
7246      * @param {String} id The column id
7247      * @return {Object} the column
7248      */
7249     getColumnById : function(id){
7250         return this.lookup[id];
7251     },
7252
7253     
7254     /**
7255      * Returns the column for a specified dataIndex.
7256      * @param {String} dataIndex The column dataIndex
7257      * @return {Object|Boolean} the column or false if not found
7258      */
7259     getColumnByDataIndex: function(dataIndex){
7260         var index = this.findColumnIndex(dataIndex);
7261         return index > -1 ? this.config[index] : false;
7262     },
7263     
7264     /**
7265      * Returns the index for a specified column id.
7266      * @param {String} id The column id
7267      * @return {Number} the index, or -1 if not found
7268      */
7269     getIndexById : function(id){
7270         for(var i = 0, len = this.config.length; i < len; i++){
7271             if(this.config[i].id == id){
7272                 return i;
7273             }
7274         }
7275         return -1;
7276     },
7277     
7278     /**
7279      * Returns the index for a specified column dataIndex.
7280      * @param {String} dataIndex The column dataIndex
7281      * @return {Number} the index, or -1 if not found
7282      */
7283     
7284     findColumnIndex : function(dataIndex){
7285         for(var i = 0, len = this.config.length; i < len; i++){
7286             if(this.config[i].dataIndex == dataIndex){
7287                 return i;
7288             }
7289         }
7290         return -1;
7291     },
7292     
7293     
7294     moveColumn : function(oldIndex, newIndex){
7295         var c = this.config[oldIndex];
7296         this.config.splice(oldIndex, 1);
7297         this.config.splice(newIndex, 0, c);
7298         this.dataMap = null;
7299         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7300     },
7301
7302     isLocked : function(colIndex){
7303         return this.config[colIndex].locked === true;
7304     },
7305
7306     setLocked : function(colIndex, value, suppressEvent){
7307         if(this.isLocked(colIndex) == value){
7308             return;
7309         }
7310         this.config[colIndex].locked = value;
7311         if(!suppressEvent){
7312             this.fireEvent("columnlockchange", this, colIndex, value);
7313         }
7314     },
7315
7316     getTotalLockedWidth : function(){
7317         var totalWidth = 0;
7318         for(var i = 0; i < this.config.length; i++){
7319             if(this.isLocked(i) && !this.isHidden(i)){
7320                 this.totalWidth += this.getColumnWidth(i);
7321             }
7322         }
7323         return totalWidth;
7324     },
7325
7326     getLockedCount : function(){
7327         for(var i = 0, len = this.config.length; i < len; i++){
7328             if(!this.isLocked(i)){
7329                 return i;
7330             }
7331         }
7332         
7333         return this.config.length;
7334     },
7335
7336     /**
7337      * Returns the number of columns.
7338      * @return {Number}
7339      */
7340     getColumnCount : function(visibleOnly){
7341         if(visibleOnly === true){
7342             var c = 0;
7343             for(var i = 0, len = this.config.length; i < len; i++){
7344                 if(!this.isHidden(i)){
7345                     c++;
7346                 }
7347             }
7348             return c;
7349         }
7350         return this.config.length;
7351     },
7352
7353     /**
7354      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7355      * @param {Function} fn
7356      * @param {Object} scope (optional)
7357      * @return {Array} result
7358      */
7359     getColumnsBy : function(fn, scope){
7360         var r = [];
7361         for(var i = 0, len = this.config.length; i < len; i++){
7362             var c = this.config[i];
7363             if(fn.call(scope||this, c, i) === true){
7364                 r[r.length] = c;
7365             }
7366         }
7367         return r;
7368     },
7369
7370     /**
7371      * Returns true if the specified column is sortable.
7372      * @param {Number} col The column index
7373      * @return {Boolean}
7374      */
7375     isSortable : function(col){
7376         if(typeof this.config[col].sortable == "undefined"){
7377             return this.defaultSortable;
7378         }
7379         return this.config[col].sortable;
7380     },
7381
7382     /**
7383      * Returns the rendering (formatting) function defined for the column.
7384      * @param {Number} col The column index.
7385      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7386      */
7387     getRenderer : function(col){
7388         if(!this.config[col].renderer){
7389             return Roo.grid.ColumnModel.defaultRenderer;
7390         }
7391         return this.config[col].renderer;
7392     },
7393
7394     /**
7395      * Sets the rendering (formatting) function for a column.
7396      * @param {Number} col The column index
7397      * @param {Function} fn The function to use to process the cell's raw data
7398      * to return HTML markup for the grid view. The render function is called with
7399      * the following parameters:<ul>
7400      * <li>Data value.</li>
7401      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7402      * <li>css A CSS style string to apply to the table cell.</li>
7403      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7404      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7405      * <li>Row index</li>
7406      * <li>Column index</li>
7407      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7408      */
7409     setRenderer : function(col, fn){
7410         this.config[col].renderer = fn;
7411     },
7412
7413     /**
7414      * Returns the width for the specified column.
7415      * @param {Number} col The column index
7416      * @return {Number}
7417      */
7418     getColumnWidth : function(col){
7419         return this.config[col].width * 1 || this.defaultWidth;
7420     },
7421
7422     /**
7423      * Sets the width for a column.
7424      * @param {Number} col The column index
7425      * @param {Number} width The new width
7426      */
7427     setColumnWidth : function(col, width, suppressEvent){
7428         this.config[col].width = width;
7429         this.totalWidth = null;
7430         if(!suppressEvent){
7431              this.fireEvent("widthchange", this, col, width);
7432         }
7433     },
7434
7435     /**
7436      * Returns the total width of all columns.
7437      * @param {Boolean} includeHidden True to include hidden column widths
7438      * @return {Number}
7439      */
7440     getTotalWidth : function(includeHidden){
7441         if(!this.totalWidth){
7442             this.totalWidth = 0;
7443             for(var i = 0, len = this.config.length; i < len; i++){
7444                 if(includeHidden || !this.isHidden(i)){
7445                     this.totalWidth += this.getColumnWidth(i);
7446                 }
7447             }
7448         }
7449         return this.totalWidth;
7450     },
7451
7452     /**
7453      * Returns the header for the specified column.
7454      * @param {Number} col The column index
7455      * @return {String}
7456      */
7457     getColumnHeader : function(col){
7458         return this.config[col].header;
7459     },
7460
7461     /**
7462      * Sets the header for a column.
7463      * @param {Number} col The column index
7464      * @param {String} header The new header
7465      */
7466     setColumnHeader : function(col, header){
7467         this.config[col].header = header;
7468         this.fireEvent("headerchange", this, col, header);
7469     },
7470
7471     /**
7472      * Returns the tooltip for the specified column.
7473      * @param {Number} col The column index
7474      * @return {String}
7475      */
7476     getColumnTooltip : function(col){
7477             return this.config[col].tooltip;
7478     },
7479     /**
7480      * Sets the tooltip for a column.
7481      * @param {Number} col The column index
7482      * @param {String} tooltip The new tooltip
7483      */
7484     setColumnTooltip : function(col, tooltip){
7485             this.config[col].tooltip = tooltip;
7486     },
7487
7488     /**
7489      * Returns the dataIndex for the specified column.
7490      * @param {Number} col The column index
7491      * @return {Number}
7492      */
7493     getDataIndex : function(col){
7494         return this.config[col].dataIndex;
7495     },
7496
7497     /**
7498      * Sets the dataIndex for a column.
7499      * @param {Number} col The column index
7500      * @param {Number} dataIndex The new dataIndex
7501      */
7502     setDataIndex : function(col, dataIndex){
7503         this.config[col].dataIndex = dataIndex;
7504     },
7505
7506     
7507     
7508     /**
7509      * Returns true if the cell is editable.
7510      * @param {Number} colIndex The column index
7511      * @param {Number} rowIndex The row index - this is nto actually used..?
7512      * @return {Boolean}
7513      */
7514     isCellEditable : function(colIndex, rowIndex){
7515         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7516     },
7517
7518     /**
7519      * Returns the editor defined for the cell/column.
7520      * return false or null to disable editing.
7521      * @param {Number} colIndex The column index
7522      * @param {Number} rowIndex The row index
7523      * @return {Object}
7524      */
7525     getCellEditor : function(colIndex, rowIndex){
7526         return this.config[colIndex].editor;
7527     },
7528
7529     /**
7530      * Sets if a column is editable.
7531      * @param {Number} col The column index
7532      * @param {Boolean} editable True if the column is editable
7533      */
7534     setEditable : function(col, editable){
7535         this.config[col].editable = editable;
7536     },
7537
7538
7539     /**
7540      * Returns true if the column is hidden.
7541      * @param {Number} colIndex The column index
7542      * @return {Boolean}
7543      */
7544     isHidden : function(colIndex){
7545         return this.config[colIndex].hidden;
7546     },
7547
7548
7549     /**
7550      * Returns true if the column width cannot be changed
7551      */
7552     isFixed : function(colIndex){
7553         return this.config[colIndex].fixed;
7554     },
7555
7556     /**
7557      * Returns true if the column can be resized
7558      * @return {Boolean}
7559      */
7560     isResizable : function(colIndex){
7561         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7562     },
7563     /**
7564      * Sets if a column is hidden.
7565      * @param {Number} colIndex The column index
7566      * @param {Boolean} hidden True if the column is hidden
7567      */
7568     setHidden : function(colIndex, hidden){
7569         this.config[colIndex].hidden = hidden;
7570         this.totalWidth = null;
7571         this.fireEvent("hiddenchange", this, colIndex, hidden);
7572     },
7573
7574     /**
7575      * Sets the editor for a column.
7576      * @param {Number} col The column index
7577      * @param {Object} editor The editor object
7578      */
7579     setEditor : function(col, editor){
7580         this.config[col].editor = editor;
7581     }
7582 });
7583
7584 Roo.grid.ColumnModel.defaultRenderer = function(value)
7585 {
7586     if(typeof value == "object") {
7587         return value;
7588     }
7589         if(typeof value == "string" && value.length < 1){
7590             return "&#160;";
7591         }
7592     
7593         return String.format("{0}", value);
7594 };
7595
7596 // Alias for backwards compatibility
7597 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7598 /*
7599  * Based on:
7600  * Ext JS Library 1.1.1
7601  * Copyright(c) 2006-2007, Ext JS, LLC.
7602  *
7603  * Originally Released Under LGPL - original licence link has changed is not relivant.
7604  *
7605  * Fork - LGPL
7606  * <script type="text/javascript">
7607  */
7608  
7609 /**
7610  * @class Roo.LoadMask
7611  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7612  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7613  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7614  * element's UpdateManager load indicator and will be destroyed after the initial load.
7615  * @constructor
7616  * Create a new LoadMask
7617  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7618  * @param {Object} config The config object
7619  */
7620 Roo.LoadMask = function(el, config){
7621     this.el = Roo.get(el);
7622     Roo.apply(this, config);
7623     if(this.store){
7624         this.store.on('beforeload', this.onBeforeLoad, this);
7625         this.store.on('load', this.onLoad, this);
7626         this.store.on('loadexception', this.onLoadException, this);
7627         this.removeMask = false;
7628     }else{
7629         var um = this.el.getUpdateManager();
7630         um.showLoadIndicator = false; // disable the default indicator
7631         um.on('beforeupdate', this.onBeforeLoad, this);
7632         um.on('update', this.onLoad, this);
7633         um.on('failure', this.onLoad, this);
7634         this.removeMask = true;
7635     }
7636 };
7637
7638 Roo.LoadMask.prototype = {
7639     /**
7640      * @cfg {Boolean} removeMask
7641      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7642      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7643      */
7644     /**
7645      * @cfg {String} msg
7646      * The text to display in a centered loading message box (defaults to 'Loading...')
7647      */
7648     msg : 'Loading...',
7649     /**
7650      * @cfg {String} msgCls
7651      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7652      */
7653     msgCls : 'x-mask-loading',
7654
7655     /**
7656      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7657      * @type Boolean
7658      */
7659     disabled: false,
7660
7661     /**
7662      * Disables the mask to prevent it from being displayed
7663      */
7664     disable : function(){
7665        this.disabled = true;
7666     },
7667
7668     /**
7669      * Enables the mask so that it can be displayed
7670      */
7671     enable : function(){
7672         this.disabled = false;
7673     },
7674     
7675     onLoadException : function()
7676     {
7677         Roo.log(arguments);
7678         
7679         if (typeof(arguments[3]) != 'undefined') {
7680             Roo.MessageBox.alert("Error loading",arguments[3]);
7681         } 
7682         /*
7683         try {
7684             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7685                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7686             }   
7687         } catch(e) {
7688             
7689         }
7690         */
7691     
7692         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7693     },
7694     // private
7695     onLoad : function()
7696     {
7697         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7698     },
7699
7700     // private
7701     onBeforeLoad : function(){
7702         if(!this.disabled){
7703             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7704         }
7705     },
7706
7707     // private
7708     destroy : function(){
7709         if(this.store){
7710             this.store.un('beforeload', this.onBeforeLoad, this);
7711             this.store.un('load', this.onLoad, this);
7712             this.store.un('loadexception', this.onLoadException, this);
7713         }else{
7714             var um = this.el.getUpdateManager();
7715             um.un('beforeupdate', this.onBeforeLoad, this);
7716             um.un('update', this.onLoad, this);
7717             um.un('failure', this.onLoad, this);
7718         }
7719     }
7720 };/*
7721  * - LGPL
7722  *
7723  * table
7724  * 
7725  */
7726
7727 /**
7728  * @class Roo.bootstrap.Table
7729  * @extends Roo.bootstrap.Component
7730  * Bootstrap Table class
7731  * @cfg {String} cls table class
7732  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7733  * @cfg {String} bgcolor Specifies the background color for a table
7734  * @cfg {Number} border Specifies whether the table cells should have borders or not
7735  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7736  * @cfg {Number} cellspacing Specifies the space between cells
7737  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7738  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7739  * @cfg {String} sortable Specifies that the table should be sortable
7740  * @cfg {String} summary Specifies a summary of the content of a table
7741  * @cfg {Number} width Specifies the width of a table
7742  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7743  * 
7744  * @cfg {boolean} striped Should the rows be alternative striped
7745  * @cfg {boolean} bordered Add borders to the table
7746  * @cfg {boolean} hover Add hover highlighting
7747  * @cfg {boolean} condensed Format condensed
7748  * @cfg {boolean} responsive Format condensed
7749  * @cfg {Boolean} loadMask (true|false) default false
7750  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7751  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7752  * @cfg {Boolean} rowSelection (true|false) default false
7753  * @cfg {Boolean} cellSelection (true|false) default false
7754  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7755  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7756  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7757  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7758  
7759  * 
7760  * @constructor
7761  * Create a new Table
7762  * @param {Object} config The config object
7763  */
7764
7765 Roo.bootstrap.Table = function(config){
7766     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7767     
7768   
7769     
7770     // BC...
7771     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7772     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7773     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7774     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7775     
7776     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7777     if (this.sm) {
7778         this.sm.grid = this;
7779         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7780         this.sm = this.selModel;
7781         this.sm.xmodule = this.xmodule || false;
7782     }
7783     
7784     if (this.cm && typeof(this.cm.config) == 'undefined') {
7785         this.colModel = new Roo.grid.ColumnModel(this.cm);
7786         this.cm = this.colModel;
7787         this.cm.xmodule = this.xmodule || false;
7788     }
7789     if (this.store) {
7790         this.store= Roo.factory(this.store, Roo.data);
7791         this.ds = this.store;
7792         this.ds.xmodule = this.xmodule || false;
7793          
7794     }
7795     if (this.footer && this.store) {
7796         this.footer.dataSource = this.ds;
7797         this.footer = Roo.factory(this.footer);
7798     }
7799     
7800     /** @private */
7801     this.addEvents({
7802         /**
7803          * @event cellclick
7804          * Fires when a cell is clicked
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         "cellclick" : true,
7812         /**
7813          * @event celldblclick
7814          * Fires when a cell is double clicked
7815          * @param {Roo.bootstrap.Table} this
7816          * @param {Roo.Element} el
7817          * @param {Number} rowIndex
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "celldblclick" : true,
7822         /**
7823          * @event rowclick
7824          * Fires when a row is clicked
7825          * @param {Roo.bootstrap.Table} this
7826          * @param {Roo.Element} el
7827          * @param {Number} rowIndex
7828          * @param {Roo.EventObject} e
7829          */
7830         "rowclick" : true,
7831         /**
7832          * @event rowdblclick
7833          * Fires when a row is double clicked
7834          * @param {Roo.bootstrap.Table} this
7835          * @param {Roo.Element} el
7836          * @param {Number} rowIndex
7837          * @param {Roo.EventObject} e
7838          */
7839         "rowdblclick" : true,
7840         /**
7841          * @event mouseover
7842          * Fires when a mouseover occur
7843          * @param {Roo.bootstrap.Table} this
7844          * @param {Roo.Element} el
7845          * @param {Number} rowIndex
7846          * @param {Number} columnIndex
7847          * @param {Roo.EventObject} e
7848          */
7849         "mouseover" : true,
7850         /**
7851          * @event mouseout
7852          * Fires when a mouseout occur
7853          * @param {Roo.bootstrap.Table} this
7854          * @param {Roo.Element} el
7855          * @param {Number} rowIndex
7856          * @param {Number} columnIndex
7857          * @param {Roo.EventObject} e
7858          */
7859         "mouseout" : true,
7860         /**
7861          * @event rowclass
7862          * Fires when a row is rendered, so you can change add a style to it.
7863          * @param {Roo.bootstrap.Table} this
7864          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7865          */
7866         'rowclass' : true,
7867           /**
7868          * @event rowsrendered
7869          * Fires when all the  rows have been rendered
7870          * @param {Roo.bootstrap.Table} this
7871          */
7872         'rowsrendered' : true,
7873         /**
7874          * @event contextmenu
7875          * The raw contextmenu event for the entire grid.
7876          * @param {Roo.EventObject} e
7877          */
7878         "contextmenu" : true,
7879         /**
7880          * @event rowcontextmenu
7881          * Fires when a row is right clicked
7882          * @param {Roo.bootstrap.Table} this
7883          * @param {Number} rowIndex
7884          * @param {Roo.EventObject} e
7885          */
7886         "rowcontextmenu" : true,
7887         /**
7888          * @event cellcontextmenu
7889          * Fires when a cell is right clicked
7890          * @param {Roo.bootstrap.Table} this
7891          * @param {Number} rowIndex
7892          * @param {Number} cellIndex
7893          * @param {Roo.EventObject} e
7894          */
7895          "cellcontextmenu" : true,
7896          /**
7897          * @event headercontextmenu
7898          * Fires when a header is right clicked
7899          * @param {Roo.bootstrap.Table} this
7900          * @param {Number} columnIndex
7901          * @param {Roo.EventObject} e
7902          */
7903         "headercontextmenu" : true
7904     });
7905 };
7906
7907 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7908     
7909     cls: false,
7910     align: false,
7911     bgcolor: false,
7912     border: false,
7913     cellpadding: false,
7914     cellspacing: false,
7915     frame: false,
7916     rules: false,
7917     sortable: false,
7918     summary: false,
7919     width: false,
7920     striped : false,
7921     scrollBody : false,
7922     bordered: false,
7923     hover:  false,
7924     condensed : false,
7925     responsive : false,
7926     sm : false,
7927     cm : false,
7928     store : false,
7929     loadMask : false,
7930     footerShow : true,
7931     headerShow : true,
7932   
7933     rowSelection : false,
7934     cellSelection : false,
7935     layout : false,
7936     
7937     // Roo.Element - the tbody
7938     mainBody: false,
7939     // Roo.Element - thead element
7940     mainHead: false,
7941     
7942     container: false, // used by gridpanel...
7943     
7944     lazyLoad : false,
7945     
7946     CSS : Roo.util.CSS,
7947     
7948     auto_hide_footer : false,
7949     
7950     getAutoCreate : function()
7951     {
7952         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7953         
7954         cfg = {
7955             tag: 'table',
7956             cls : 'table',
7957             cn : []
7958         };
7959         if (this.scrollBody) {
7960             cfg.cls += ' table-body-fixed';
7961         }    
7962         if (this.striped) {
7963             cfg.cls += ' table-striped';
7964         }
7965         
7966         if (this.hover) {
7967             cfg.cls += ' table-hover';
7968         }
7969         if (this.bordered) {
7970             cfg.cls += ' table-bordered';
7971         }
7972         if (this.condensed) {
7973             cfg.cls += ' table-condensed';
7974         }
7975         if (this.responsive) {
7976             cfg.cls += ' table-responsive';
7977         }
7978         
7979         if (this.cls) {
7980             cfg.cls+=  ' ' +this.cls;
7981         }
7982         
7983         // this lot should be simplifed...
7984         var _t = this;
7985         var cp = [
7986             'align',
7987             'bgcolor',
7988             'border',
7989             'cellpadding',
7990             'cellspacing',
7991             'frame',
7992             'rules',
7993             'sortable',
7994             'summary',
7995             'width'
7996         ].forEach(function(k) {
7997             if (_t[k]) {
7998                 cfg[k] = _t[k];
7999             }
8000         });
8001         
8002         
8003         if (this.layout) {
8004             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8005         }
8006         
8007         if(this.store || this.cm){
8008             if(this.headerShow){
8009                 cfg.cn.push(this.renderHeader());
8010             }
8011             
8012             cfg.cn.push(this.renderBody());
8013             
8014             if(this.footerShow){
8015                 cfg.cn.push(this.renderFooter());
8016             }
8017             // where does this come from?
8018             //cfg.cls+=  ' TableGrid';
8019         }
8020         
8021         return { cn : [ cfg ] };
8022     },
8023     
8024     initEvents : function()
8025     {   
8026         if(!this.store || !this.cm){
8027             return;
8028         }
8029         if (this.selModel) {
8030             this.selModel.initEvents();
8031         }
8032         
8033         
8034         //Roo.log('initEvents with ds!!!!');
8035         
8036         this.mainBody = this.el.select('tbody', true).first();
8037         this.mainHead = this.el.select('thead', true).first();
8038         this.mainFoot = this.el.select('tfoot', true).first();
8039         
8040         
8041         
8042         var _this = this;
8043         
8044         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8045             e.on('click', _this.sort, _this);
8046         });
8047         
8048         this.mainBody.on("click", this.onClick, this);
8049         this.mainBody.on("dblclick", this.onDblClick, this);
8050         
8051         // why is this done????? = it breaks dialogs??
8052         //this.parent().el.setStyle('position', 'relative');
8053         
8054         
8055         if (this.footer) {
8056             this.footer.parentId = this.id;
8057             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8058             
8059             if(this.lazyLoad){
8060                 this.el.select('tfoot tr td').first().addClass('hide');
8061             }
8062         } 
8063         
8064         if(this.loadMask) {
8065             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8066         }
8067         
8068         this.store.on('load', this.onLoad, this);
8069         this.store.on('beforeload', this.onBeforeLoad, this);
8070         this.store.on('update', this.onUpdate, this);
8071         this.store.on('add', this.onAdd, this);
8072         this.store.on("clear", this.clear, this);
8073         
8074         this.el.on("contextmenu", this.onContextMenu, this);
8075         
8076         this.mainBody.on('scroll', this.onBodyScroll, this);
8077         
8078         this.cm.on("headerchange", this.onHeaderChange, this);
8079         
8080         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8081         
8082     },
8083     
8084     onContextMenu : function(e, t)
8085     {
8086         this.processEvent("contextmenu", e);
8087     },
8088     
8089     processEvent : function(name, e)
8090     {
8091         if (name != 'touchstart' ) {
8092             this.fireEvent(name, e);    
8093         }
8094         
8095         var t = e.getTarget();
8096         
8097         var cell = Roo.get(t);
8098         
8099         if(!cell){
8100             return;
8101         }
8102         
8103         if(cell.findParent('tfoot', false, true)){
8104             return;
8105         }
8106         
8107         if(cell.findParent('thead', false, true)){
8108             
8109             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8110                 cell = Roo.get(t).findParent('th', false, true);
8111                 if (!cell) {
8112                     Roo.log("failed to find th in thead?");
8113                     Roo.log(e.getTarget());
8114                     return;
8115                 }
8116             }
8117             
8118             var cellIndex = cell.dom.cellIndex;
8119             
8120             var ename = name == 'touchstart' ? 'click' : name;
8121             this.fireEvent("header" + ename, this, cellIndex, e);
8122             
8123             return;
8124         }
8125         
8126         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8127             cell = Roo.get(t).findParent('td', false, true);
8128             if (!cell) {
8129                 Roo.log("failed to find th in tbody?");
8130                 Roo.log(e.getTarget());
8131                 return;
8132             }
8133         }
8134         
8135         var row = cell.findParent('tr', false, true);
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = row.dom.rowIndex - 1;
8138         
8139         if(row !== false){
8140             
8141             this.fireEvent("row" + name, this, rowIndex, e);
8142             
8143             if(cell !== false){
8144             
8145                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8146             }
8147         }
8148         
8149     },
8150     
8151     onMouseover : function(e, el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         var row = cell.findParent('tr', false, true);
8164         var cellIndex = cell.dom.cellIndex;
8165         var rowIndex = row.dom.rowIndex - 1; // start from 0
8166         
8167         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8168         
8169     },
8170     
8171     onMouseout : function(e, el)
8172     {
8173         var cell = Roo.get(el);
8174         
8175         if(!cell){
8176             return;
8177         }
8178         
8179         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8180             cell = cell.findParent('td', false, true);
8181         }
8182         
8183         var row = cell.findParent('tr', false, true);
8184         var cellIndex = cell.dom.cellIndex;
8185         var rowIndex = row.dom.rowIndex - 1; // start from 0
8186         
8187         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8188         
8189     },
8190     
8191     onClick : function(e, el)
8192     {
8193         var cell = Roo.get(el);
8194         
8195         if(!cell || (!this.cellSelection && !this.rowSelection)){
8196             return;
8197         }
8198         
8199         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8200             cell = cell.findParent('td', false, true);
8201         }
8202         
8203         if(!cell || typeof(cell) == 'undefined'){
8204             return;
8205         }
8206         
8207         var row = cell.findParent('tr', false, true);
8208         
8209         if(!row || typeof(row) == 'undefined'){
8210             return;
8211         }
8212         
8213         var cellIndex = cell.dom.cellIndex;
8214         var rowIndex = this.getRowIndex(row);
8215         
8216         // why??? - should these not be based on SelectionModel?
8217         if(this.cellSelection){
8218             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8219         }
8220         
8221         if(this.rowSelection){
8222             this.fireEvent('rowclick', this, row, rowIndex, e);
8223         }
8224         
8225         
8226     },
8227         
8228     onDblClick : function(e,el)
8229     {
8230         var cell = Roo.get(el);
8231         
8232         if(!cell || (!this.cellSelection && !this.rowSelection)){
8233             return;
8234         }
8235         
8236         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8237             cell = cell.findParent('td', false, true);
8238         }
8239         
8240         if(!cell || typeof(cell) == 'undefined'){
8241             return;
8242         }
8243         
8244         var row = cell.findParent('tr', false, true);
8245         
8246         if(!row || typeof(row) == 'undefined'){
8247             return;
8248         }
8249         
8250         var cellIndex = cell.dom.cellIndex;
8251         var rowIndex = this.getRowIndex(row);
8252         
8253         if(this.cellSelection){
8254             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8255         }
8256         
8257         if(this.rowSelection){
8258             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8259         }
8260     },
8261     
8262     sort : function(e,el)
8263     {
8264         var col = Roo.get(el);
8265         
8266         if(!col.hasClass('sortable')){
8267             return;
8268         }
8269         
8270         var sort = col.attr('sort');
8271         var dir = 'ASC';
8272         
8273         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8274             dir = 'DESC';
8275         }
8276         
8277         this.store.sortInfo = {field : sort, direction : dir};
8278         
8279         if (this.footer) {
8280             Roo.log("calling footer first");
8281             this.footer.onClick('first');
8282         } else {
8283         
8284             this.store.load({ params : { start : 0 } });
8285         }
8286     },
8287     
8288     renderHeader : function()
8289     {
8290         var header = {
8291             tag: 'thead',
8292             cn : []
8293         };
8294         
8295         var cm = this.cm;
8296         this.totalWidth = 0;
8297         
8298         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8299             
8300             var config = cm.config[i];
8301             
8302             var c = {
8303                 tag: 'th',
8304                 cls : 'x-hcol-' + i,
8305                 style : '',
8306                 html: cm.getColumnHeader(i)
8307             };
8308             
8309             var hh = '';
8310             
8311             if(typeof(config.sortable) != 'undefined' && config.sortable){
8312                 c.cls = 'sortable';
8313                 c.html = '<i class="glyphicon"></i>' + c.html;
8314             }
8315             
8316             // could use BS4 hidden-..-down 
8317             
8318             if(typeof(config.lgHeader) != 'undefined'){
8319                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8320             }
8321             
8322             if(typeof(config.mdHeader) != 'undefined'){
8323                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8324             }
8325             
8326             if(typeof(config.smHeader) != 'undefined'){
8327                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8328             }
8329             
8330             if(typeof(config.xsHeader) != 'undefined'){
8331                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8332             }
8333             
8334             if(hh.length){
8335                 c.html = hh;
8336             }
8337             
8338             if(typeof(config.tooltip) != 'undefined'){
8339                 c.tooltip = config.tooltip;
8340             }
8341             
8342             if(typeof(config.colspan) != 'undefined'){
8343                 c.colspan = config.colspan;
8344             }
8345             
8346             if(typeof(config.hidden) != 'undefined' && config.hidden){
8347                 c.style += ' display:none;';
8348             }
8349             
8350             if(typeof(config.dataIndex) != 'undefined'){
8351                 c.sort = config.dataIndex;
8352             }
8353             
8354            
8355             
8356             if(typeof(config.align) != 'undefined' && config.align.length){
8357                 c.style += ' text-align:' + config.align + ';';
8358             }
8359             
8360             if(typeof(config.width) != 'undefined'){
8361                 c.style += ' width:' + config.width + 'px;';
8362                 this.totalWidth += config.width;
8363             } else {
8364                 this.totalWidth += 100; // assume minimum of 100 per column?
8365             }
8366             
8367             if(typeof(config.cls) != 'undefined'){
8368                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8369             }
8370             
8371             ['xs','sm','md','lg'].map(function(size){
8372                 
8373                 if(typeof(config[size]) == 'undefined'){
8374                     return;
8375                 }
8376                  
8377                 if (!config[size]) { // 0 = hidden
8378                     // BS 4 '0' is treated as hide that column and below.
8379                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8380                     return;
8381                 }
8382                 
8383                 c.cls += ' col-' + size + '-' + config[size] + (
8384                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8385                 );
8386                 
8387                 
8388             });
8389             
8390             header.cn.push(c)
8391         }
8392         
8393         return header;
8394     },
8395     
8396     renderBody : function()
8397     {
8398         var body = {
8399             tag: 'tbody',
8400             cn : [
8401                 {
8402                     tag: 'tr',
8403                     cn : [
8404                         {
8405                             tag : 'td',
8406                             colspan :  this.cm.getColumnCount()
8407                         }
8408                     ]
8409                 }
8410             ]
8411         };
8412         
8413         return body;
8414     },
8415     
8416     renderFooter : function()
8417     {
8418         var footer = {
8419             tag: 'tfoot',
8420             cn : [
8421                 {
8422                     tag: 'tr',
8423                     cn : [
8424                         {
8425                             tag : 'td',
8426                             colspan :  this.cm.getColumnCount()
8427                         }
8428                     ]
8429                 }
8430             ]
8431         };
8432         
8433         return footer;
8434     },
8435     
8436     
8437     
8438     onLoad : function()
8439     {
8440 //        Roo.log('ds onload');
8441         this.clear();
8442         
8443         var _this = this;
8444         var cm = this.cm;
8445         var ds = this.store;
8446         
8447         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8448             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8449             if (_this.store.sortInfo) {
8450                     
8451                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8452                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8453                 }
8454                 
8455                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8456                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8457                 }
8458             }
8459         });
8460         
8461         var tbody =  this.mainBody;
8462               
8463         if(ds.getCount() > 0){
8464             ds.data.each(function(d,rowIndex){
8465                 var row =  this.renderRow(cm, ds, rowIndex);
8466                 
8467                 tbody.createChild(row);
8468                 
8469                 var _this = this;
8470                 
8471                 if(row.cellObjects.length){
8472                     Roo.each(row.cellObjects, function(r){
8473                         _this.renderCellObject(r);
8474                     })
8475                 }
8476                 
8477             }, this);
8478         }
8479         
8480         var tfoot = this.el.select('tfoot', true).first();
8481         
8482         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8483             
8484             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8485             
8486             var total = this.ds.getTotalCount();
8487             
8488             if(this.footer.pageSize < total){
8489                 this.mainFoot.show();
8490             }
8491         }
8492         
8493         Roo.each(this.el.select('tbody td', true).elements, function(e){
8494             e.on('mouseover', _this.onMouseover, _this);
8495         });
8496         
8497         Roo.each(this.el.select('tbody td', true).elements, function(e){
8498             e.on('mouseout', _this.onMouseout, _this);
8499         });
8500         this.fireEvent('rowsrendered', this);
8501         
8502         this.autoSize();
8503     },
8504     
8505     
8506     onUpdate : function(ds,record)
8507     {
8508         this.refreshRow(record);
8509         this.autoSize();
8510     },
8511     
8512     onRemove : function(ds, record, index, isUpdate){
8513         if(isUpdate !== true){
8514             this.fireEvent("beforerowremoved", this, index, record);
8515         }
8516         var bt = this.mainBody.dom;
8517         
8518         var rows = this.el.select('tbody > tr', true).elements;
8519         
8520         if(typeof(rows[index]) != 'undefined'){
8521             bt.removeChild(rows[index].dom);
8522         }
8523         
8524 //        if(bt.rows[index]){
8525 //            bt.removeChild(bt.rows[index]);
8526 //        }
8527         
8528         if(isUpdate !== true){
8529             //this.stripeRows(index);
8530             //this.syncRowHeights(index, index);
8531             //this.layout();
8532             this.fireEvent("rowremoved", this, index, record);
8533         }
8534     },
8535     
8536     onAdd : function(ds, records, rowIndex)
8537     {
8538         //Roo.log('on Add called');
8539         // - note this does not handle multiple adding very well..
8540         var bt = this.mainBody.dom;
8541         for (var i =0 ; i < records.length;i++) {
8542             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8543             //Roo.log(records[i]);
8544             //Roo.log(this.store.getAt(rowIndex+i));
8545             this.insertRow(this.store, rowIndex + i, false);
8546             return;
8547         }
8548         
8549     },
8550     
8551     
8552     refreshRow : function(record){
8553         var ds = this.store, index;
8554         if(typeof record == 'number'){
8555             index = record;
8556             record = ds.getAt(index);
8557         }else{
8558             index = ds.indexOf(record);
8559             if (index < 0) {
8560                 return; // should not happen - but seems to 
8561             }
8562         }
8563         this.insertRow(ds, index, true);
8564         this.autoSize();
8565         this.onRemove(ds, record, index+1, true);
8566         this.autoSize();
8567         //this.syncRowHeights(index, index);
8568         //this.layout();
8569         this.fireEvent("rowupdated", this, index, record);
8570     },
8571     
8572     insertRow : function(dm, rowIndex, isUpdate){
8573         
8574         if(!isUpdate){
8575             this.fireEvent("beforerowsinserted", this, rowIndex);
8576         }
8577             //var s = this.getScrollState();
8578         var row = this.renderRow(this.cm, this.store, rowIndex);
8579         // insert before rowIndex..
8580         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8581         
8582         var _this = this;
8583                 
8584         if(row.cellObjects.length){
8585             Roo.each(row.cellObjects, function(r){
8586                 _this.renderCellObject(r);
8587             })
8588         }
8589             
8590         if(!isUpdate){
8591             this.fireEvent("rowsinserted", this, rowIndex);
8592             //this.syncRowHeights(firstRow, lastRow);
8593             //this.stripeRows(firstRow);
8594             //this.layout();
8595         }
8596         
8597     },
8598     
8599     
8600     getRowDom : function(rowIndex)
8601     {
8602         var rows = this.el.select('tbody > tr', true).elements;
8603         
8604         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8605         
8606     },
8607     // returns the object tree for a tr..
8608   
8609     
8610     renderRow : function(cm, ds, rowIndex) 
8611     {
8612         var d = ds.getAt(rowIndex);
8613         
8614         var row = {
8615             tag : 'tr',
8616             cls : 'x-row-' + rowIndex,
8617             cn : []
8618         };
8619             
8620         var cellObjects = [];
8621         
8622         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8623             var config = cm.config[i];
8624             
8625             var renderer = cm.getRenderer(i);
8626             var value = '';
8627             var id = false;
8628             
8629             if(typeof(renderer) !== 'undefined'){
8630                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8631             }
8632             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8633             // and are rendered into the cells after the row is rendered - using the id for the element.
8634             
8635             if(typeof(value) === 'object'){
8636                 id = Roo.id();
8637                 cellObjects.push({
8638                     container : id,
8639                     cfg : value 
8640                 })
8641             }
8642             
8643             var rowcfg = {
8644                 record: d,
8645                 rowIndex : rowIndex,
8646                 colIndex : i,
8647                 rowClass : ''
8648             };
8649
8650             this.fireEvent('rowclass', this, rowcfg);
8651             
8652             var td = {
8653                 tag: 'td',
8654                 cls : rowcfg.rowClass + ' x-col-' + i,
8655                 style: '',
8656                 html: (typeof(value) === 'object') ? '' : value
8657             };
8658             
8659             if (id) {
8660                 td.id = id;
8661             }
8662             
8663             if(typeof(config.colspan) != 'undefined'){
8664                 td.colspan = config.colspan;
8665             }
8666             
8667             if(typeof(config.hidden) != 'undefined' && config.hidden){
8668                 td.style += ' display:none;';
8669             }
8670             
8671             if(typeof(config.align) != 'undefined' && config.align.length){
8672                 td.style += ' text-align:' + config.align + ';';
8673             }
8674             if(typeof(config.valign) != 'undefined' && config.valign.length){
8675                 td.style += ' vertical-align:' + config.valign + ';';
8676             }
8677             
8678             if(typeof(config.width) != 'undefined'){
8679                 td.style += ' width:' +  config.width + 'px;';
8680             }
8681             
8682             if(typeof(config.cursor) != 'undefined'){
8683                 td.style += ' cursor:' +  config.cursor + ';';
8684             }
8685             
8686             if(typeof(config.cls) != 'undefined'){
8687                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8688             }
8689             
8690             ['xs','sm','md','lg'].map(function(size){
8691                 
8692                 if(typeof(config[size]) == 'undefined'){
8693                     return;
8694                 }
8695                 
8696                 
8697                   
8698                 if (!config[size]) { // 0 = hidden
8699                     // BS 4 '0' is treated as hide that column and below.
8700                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8701                     return;
8702                 }
8703                 
8704                 td.cls += ' col-' + size + '-' + config[size] + (
8705                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8706                 );
8707                  
8708
8709             });
8710             
8711             row.cn.push(td);
8712            
8713         }
8714         
8715         row.cellObjects = cellObjects;
8716         
8717         return row;
8718           
8719     },
8720     
8721     
8722     
8723     onBeforeLoad : function()
8724     {
8725         
8726     },
8727      /**
8728      * Remove all rows
8729      */
8730     clear : function()
8731     {
8732         this.el.select('tbody', true).first().dom.innerHTML = '';
8733     },
8734     /**
8735      * Show or hide a row.
8736      * @param {Number} rowIndex to show or hide
8737      * @param {Boolean} state hide
8738      */
8739     setRowVisibility : function(rowIndex, state)
8740     {
8741         var bt = this.mainBody.dom;
8742         
8743         var rows = this.el.select('tbody > tr', true).elements;
8744         
8745         if(typeof(rows[rowIndex]) == 'undefined'){
8746             return;
8747         }
8748         rows[rowIndex].dom.style.display = state ? '' : 'none';
8749     },
8750     
8751     
8752     getSelectionModel : function(){
8753         if(!this.selModel){
8754             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8755         }
8756         return this.selModel;
8757     },
8758     /*
8759      * Render the Roo.bootstrap object from renderder
8760      */
8761     renderCellObject : function(r)
8762     {
8763         var _this = this;
8764         
8765         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8766         
8767         var t = r.cfg.render(r.container);
8768         
8769         if(r.cfg.cn){
8770             Roo.each(r.cfg.cn, function(c){
8771                 var child = {
8772                     container: t.getChildContainer(),
8773                     cfg: c
8774                 };
8775                 _this.renderCellObject(child);
8776             })
8777         }
8778     },
8779     
8780     getRowIndex : function(row)
8781     {
8782         var rowIndex = -1;
8783         
8784         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8785             if(el != row){
8786                 return;
8787             }
8788             
8789             rowIndex = index;
8790         });
8791         
8792         return rowIndex;
8793     },
8794      /**
8795      * Returns the grid's underlying element = used by panel.Grid
8796      * @return {Element} The element
8797      */
8798     getGridEl : function(){
8799         return this.el;
8800     },
8801      /**
8802      * Forces a resize - used by panel.Grid
8803      * @return {Element} The element
8804      */
8805     autoSize : function()
8806     {
8807         //var ctr = Roo.get(this.container.dom.parentElement);
8808         var ctr = Roo.get(this.el.dom);
8809         
8810         var thd = this.getGridEl().select('thead',true).first();
8811         var tbd = this.getGridEl().select('tbody', true).first();
8812         var tfd = this.getGridEl().select('tfoot', true).first();
8813         
8814         var cw = ctr.getWidth();
8815         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8816         
8817         if (tbd) {
8818             
8819             tbd.setWidth(ctr.getWidth());
8820             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8821             // this needs fixing for various usage - currently only hydra job advers I think..
8822             //tdb.setHeight(
8823             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8824             //); 
8825             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8826             cw -= barsize;
8827         }
8828         cw = Math.max(cw, this.totalWidth);
8829         this.getGridEl().select('tbody tr',true).setWidth(cw);
8830         
8831         // resize 'expandable coloumn?
8832         
8833         return; // we doe not have a view in this design..
8834         
8835     },
8836     onBodyScroll: function()
8837     {
8838         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8839         if(this.mainHead){
8840             this.mainHead.setStyle({
8841                 'position' : 'relative',
8842                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8843             });
8844         }
8845         
8846         if(this.lazyLoad){
8847             
8848             var scrollHeight = this.mainBody.dom.scrollHeight;
8849             
8850             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8851             
8852             var height = this.mainBody.getHeight();
8853             
8854             if(scrollHeight - height == scrollTop) {
8855                 
8856                 var total = this.ds.getTotalCount();
8857                 
8858                 if(this.footer.cursor + this.footer.pageSize < total){
8859                     
8860                     this.footer.ds.load({
8861                         params : {
8862                             start : this.footer.cursor + this.footer.pageSize,
8863                             limit : this.footer.pageSize
8864                         },
8865                         add : true
8866                     });
8867                 }
8868             }
8869             
8870         }
8871     },
8872     
8873     onHeaderChange : function()
8874     {
8875         var header = this.renderHeader();
8876         var table = this.el.select('table', true).first();
8877         
8878         this.mainHead.remove();
8879         this.mainHead = table.createChild(header, this.mainBody, false);
8880     },
8881     
8882     onHiddenChange : function(colModel, colIndex, hidden)
8883     {
8884         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8885         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8886         
8887         this.CSS.updateRule(thSelector, "display", "");
8888         this.CSS.updateRule(tdSelector, "display", "");
8889         
8890         if(hidden){
8891             this.CSS.updateRule(thSelector, "display", "none");
8892             this.CSS.updateRule(tdSelector, "display", "none");
8893         }
8894         
8895         this.onHeaderChange();
8896         this.onLoad();
8897     },
8898     
8899     setColumnWidth: function(col_index, width)
8900     {
8901         // width = "md-2 xs-2..."
8902         if(!this.colModel.config[col_index]) {
8903             return;
8904         }
8905         
8906         var w = width.split(" ");
8907         
8908         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8909         
8910         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8911         
8912         
8913         for(var j = 0; j < w.length; j++) {
8914             
8915             if(!w[j]) {
8916                 continue;
8917             }
8918             
8919             var size_cls = w[j].split("-");
8920             
8921             if(!Number.isInteger(size_cls[1] * 1)) {
8922                 continue;
8923             }
8924             
8925             if(!this.colModel.config[col_index][size_cls[0]]) {
8926                 continue;
8927             }
8928             
8929             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8930                 continue;
8931             }
8932             
8933             h_row[0].classList.replace(
8934                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8935                 "col-"+size_cls[0]+"-"+size_cls[1]
8936             );
8937             
8938             for(var i = 0; i < rows.length; i++) {
8939                 
8940                 var size_cls = w[j].split("-");
8941                 
8942                 if(!Number.isInteger(size_cls[1] * 1)) {
8943                     continue;
8944                 }
8945                 
8946                 if(!this.colModel.config[col_index][size_cls[0]]) {
8947                     continue;
8948                 }
8949                 
8950                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8951                     continue;
8952                 }
8953                 
8954                 rows[i].classList.replace(
8955                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8956                     "col-"+size_cls[0]+"-"+size_cls[1]
8957                 );
8958             }
8959             
8960             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8961         }
8962     }
8963 });
8964
8965  
8966
8967  /*
8968  * - LGPL
8969  *
8970  * table cell
8971  * 
8972  */
8973
8974 /**
8975  * @class Roo.bootstrap.TableCell
8976  * @extends Roo.bootstrap.Component
8977  * Bootstrap TableCell class
8978  * @cfg {String} html cell contain text
8979  * @cfg {String} cls cell class
8980  * @cfg {String} tag cell tag (td|th) default td
8981  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8982  * @cfg {String} align Aligns the content in a cell
8983  * @cfg {String} axis Categorizes cells
8984  * @cfg {String} bgcolor Specifies the background color of a cell
8985  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8986  * @cfg {Number} colspan Specifies the number of columns a cell should span
8987  * @cfg {String} headers Specifies one or more header cells a cell is related to
8988  * @cfg {Number} height Sets the height of a cell
8989  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8990  * @cfg {Number} rowspan Sets the number of rows a cell should span
8991  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8992  * @cfg {String} valign Vertical aligns the content in a cell
8993  * @cfg {Number} width Specifies the width of a cell
8994  * 
8995  * @constructor
8996  * Create a new TableCell
8997  * @param {Object} config The config object
8998  */
8999
9000 Roo.bootstrap.TableCell = function(config){
9001     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9002 };
9003
9004 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9005     
9006     html: false,
9007     cls: false,
9008     tag: false,
9009     abbr: false,
9010     align: false,
9011     axis: false,
9012     bgcolor: false,
9013     charoff: false,
9014     colspan: false,
9015     headers: false,
9016     height: false,
9017     nowrap: false,
9018     rowspan: false,
9019     scope: false,
9020     valign: false,
9021     width: false,
9022     
9023     
9024     getAutoCreate : function(){
9025         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9026         
9027         cfg = {
9028             tag: 'td'
9029         };
9030         
9031         if(this.tag){
9032             cfg.tag = this.tag;
9033         }
9034         
9035         if (this.html) {
9036             cfg.html=this.html
9037         }
9038         if (this.cls) {
9039             cfg.cls=this.cls
9040         }
9041         if (this.abbr) {
9042             cfg.abbr=this.abbr
9043         }
9044         if (this.align) {
9045             cfg.align=this.align
9046         }
9047         if (this.axis) {
9048             cfg.axis=this.axis
9049         }
9050         if (this.bgcolor) {
9051             cfg.bgcolor=this.bgcolor
9052         }
9053         if (this.charoff) {
9054             cfg.charoff=this.charoff
9055         }
9056         if (this.colspan) {
9057             cfg.colspan=this.colspan
9058         }
9059         if (this.headers) {
9060             cfg.headers=this.headers
9061         }
9062         if (this.height) {
9063             cfg.height=this.height
9064         }
9065         if (this.nowrap) {
9066             cfg.nowrap=this.nowrap
9067         }
9068         if (this.rowspan) {
9069             cfg.rowspan=this.rowspan
9070         }
9071         if (this.scope) {
9072             cfg.scope=this.scope
9073         }
9074         if (this.valign) {
9075             cfg.valign=this.valign
9076         }
9077         if (this.width) {
9078             cfg.width=this.width
9079         }
9080         
9081         
9082         return cfg;
9083     }
9084    
9085 });
9086
9087  
9088
9089  /*
9090  * - LGPL
9091  *
9092  * table row
9093  * 
9094  */
9095
9096 /**
9097  * @class Roo.bootstrap.TableRow
9098  * @extends Roo.bootstrap.Component
9099  * Bootstrap TableRow class
9100  * @cfg {String} cls row class
9101  * @cfg {String} align Aligns the content in a table row
9102  * @cfg {String} bgcolor Specifies a background color for a table row
9103  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9104  * @cfg {String} valign Vertical aligns the content in a table row
9105  * 
9106  * @constructor
9107  * Create a new TableRow
9108  * @param {Object} config The config object
9109  */
9110
9111 Roo.bootstrap.TableRow = function(config){
9112     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9113 };
9114
9115 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9116     
9117     cls: false,
9118     align: false,
9119     bgcolor: false,
9120     charoff: false,
9121     valign: false,
9122     
9123     getAutoCreate : function(){
9124         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9125         
9126         cfg = {
9127             tag: 'tr'
9128         };
9129             
9130         if(this.cls){
9131             cfg.cls = this.cls;
9132         }
9133         if(this.align){
9134             cfg.align = this.align;
9135         }
9136         if(this.bgcolor){
9137             cfg.bgcolor = this.bgcolor;
9138         }
9139         if(this.charoff){
9140             cfg.charoff = this.charoff;
9141         }
9142         if(this.valign){
9143             cfg.valign = this.valign;
9144         }
9145         
9146         return cfg;
9147     }
9148    
9149 });
9150
9151  
9152
9153  /*
9154  * - LGPL
9155  *
9156  * table body
9157  * 
9158  */
9159
9160 /**
9161  * @class Roo.bootstrap.TableBody
9162  * @extends Roo.bootstrap.Component
9163  * Bootstrap TableBody class
9164  * @cfg {String} cls element class
9165  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9166  * @cfg {String} align Aligns the content inside the element
9167  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9168  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9169  * 
9170  * @constructor
9171  * Create a new TableBody
9172  * @param {Object} config The config object
9173  */
9174
9175 Roo.bootstrap.TableBody = function(config){
9176     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9177 };
9178
9179 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9180     
9181     cls: false,
9182     tag: false,
9183     align: false,
9184     charoff: false,
9185     valign: false,
9186     
9187     getAutoCreate : function(){
9188         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9189         
9190         cfg = {
9191             tag: 'tbody'
9192         };
9193             
9194         if (this.cls) {
9195             cfg.cls=this.cls
9196         }
9197         if(this.tag){
9198             cfg.tag = this.tag;
9199         }
9200         
9201         if(this.align){
9202             cfg.align = this.align;
9203         }
9204         if(this.charoff){
9205             cfg.charoff = this.charoff;
9206         }
9207         if(this.valign){
9208             cfg.valign = this.valign;
9209         }
9210         
9211         return cfg;
9212     }
9213     
9214     
9215 //    initEvents : function()
9216 //    {
9217 //        
9218 //        if(!this.store){
9219 //            return;
9220 //        }
9221 //        
9222 //        this.store = Roo.factory(this.store, Roo.data);
9223 //        this.store.on('load', this.onLoad, this);
9224 //        
9225 //        this.store.load();
9226 //        
9227 //    },
9228 //    
9229 //    onLoad: function () 
9230 //    {   
9231 //        this.fireEvent('load', this);
9232 //    }
9233 //    
9234 //   
9235 });
9236
9237  
9238
9239  /*
9240  * Based on:
9241  * Ext JS Library 1.1.1
9242  * Copyright(c) 2006-2007, Ext JS, LLC.
9243  *
9244  * Originally Released Under LGPL - original licence link has changed is not relivant.
9245  *
9246  * Fork - LGPL
9247  * <script type="text/javascript">
9248  */
9249
9250 // as we use this in bootstrap.
9251 Roo.namespace('Roo.form');
9252  /**
9253  * @class Roo.form.Action
9254  * Internal Class used to handle form actions
9255  * @constructor
9256  * @param {Roo.form.BasicForm} el The form element or its id
9257  * @param {Object} config Configuration options
9258  */
9259
9260  
9261  
9262 // define the action interface
9263 Roo.form.Action = function(form, options){
9264     this.form = form;
9265     this.options = options || {};
9266 };
9267 /**
9268  * Client Validation Failed
9269  * @const 
9270  */
9271 Roo.form.Action.CLIENT_INVALID = 'client';
9272 /**
9273  * Server Validation Failed
9274  * @const 
9275  */
9276 Roo.form.Action.SERVER_INVALID = 'server';
9277  /**
9278  * Connect to Server Failed
9279  * @const 
9280  */
9281 Roo.form.Action.CONNECT_FAILURE = 'connect';
9282 /**
9283  * Reading Data from Server Failed
9284  * @const 
9285  */
9286 Roo.form.Action.LOAD_FAILURE = 'load';
9287
9288 Roo.form.Action.prototype = {
9289     type : 'default',
9290     failureType : undefined,
9291     response : undefined,
9292     result : undefined,
9293
9294     // interface method
9295     run : function(options){
9296
9297     },
9298
9299     // interface method
9300     success : function(response){
9301
9302     },
9303
9304     // interface method
9305     handleResponse : function(response){
9306
9307     },
9308
9309     // default connection failure
9310     failure : function(response){
9311         
9312         this.response = response;
9313         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9314         this.form.afterAction(this, false);
9315     },
9316
9317     processResponse : function(response){
9318         this.response = response;
9319         if(!response.responseText){
9320             return true;
9321         }
9322         this.result = this.handleResponse(response);
9323         return this.result;
9324     },
9325
9326     // utility functions used internally
9327     getUrl : function(appendParams){
9328         var url = this.options.url || this.form.url || this.form.el.dom.action;
9329         if(appendParams){
9330             var p = this.getParams();
9331             if(p){
9332                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9333             }
9334         }
9335         return url;
9336     },
9337
9338     getMethod : function(){
9339         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9340     },
9341
9342     getParams : function(){
9343         var bp = this.form.baseParams;
9344         var p = this.options.params;
9345         if(p){
9346             if(typeof p == "object"){
9347                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9348             }else if(typeof p == 'string' && bp){
9349                 p += '&' + Roo.urlEncode(bp);
9350             }
9351         }else if(bp){
9352             p = Roo.urlEncode(bp);
9353         }
9354         return p;
9355     },
9356
9357     createCallback : function(){
9358         return {
9359             success: this.success,
9360             failure: this.failure,
9361             scope: this,
9362             timeout: (this.form.timeout*1000),
9363             upload: this.form.fileUpload ? this.success : undefined
9364         };
9365     }
9366 };
9367
9368 Roo.form.Action.Submit = function(form, options){
9369     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9370 };
9371
9372 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9373     type : 'submit',
9374
9375     haveProgress : false,
9376     uploadComplete : false,
9377     
9378     // uploadProgress indicator.
9379     uploadProgress : function()
9380     {
9381         if (!this.form.progressUrl) {
9382             return;
9383         }
9384         
9385         if (!this.haveProgress) {
9386             Roo.MessageBox.progress("Uploading", "Uploading");
9387         }
9388         if (this.uploadComplete) {
9389            Roo.MessageBox.hide();
9390            return;
9391         }
9392         
9393         this.haveProgress = true;
9394    
9395         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9396         
9397         var c = new Roo.data.Connection();
9398         c.request({
9399             url : this.form.progressUrl,
9400             params: {
9401                 id : uid
9402             },
9403             method: 'GET',
9404             success : function(req){
9405                //console.log(data);
9406                 var rdata = false;
9407                 var edata;
9408                 try  {
9409                    rdata = Roo.decode(req.responseText)
9410                 } catch (e) {
9411                     Roo.log("Invalid data from server..");
9412                     Roo.log(edata);
9413                     return;
9414                 }
9415                 if (!rdata || !rdata.success) {
9416                     Roo.log(rdata);
9417                     Roo.MessageBox.alert(Roo.encode(rdata));
9418                     return;
9419                 }
9420                 var data = rdata.data;
9421                 
9422                 if (this.uploadComplete) {
9423                    Roo.MessageBox.hide();
9424                    return;
9425                 }
9426                    
9427                 if (data){
9428                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9429                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9430                     );
9431                 }
9432                 this.uploadProgress.defer(2000,this);
9433             },
9434        
9435             failure: function(data) {
9436                 Roo.log('progress url failed ');
9437                 Roo.log(data);
9438             },
9439             scope : this
9440         });
9441            
9442     },
9443     
9444     
9445     run : function()
9446     {
9447         // run get Values on the form, so it syncs any secondary forms.
9448         this.form.getValues();
9449         
9450         var o = this.options;
9451         var method = this.getMethod();
9452         var isPost = method == 'POST';
9453         if(o.clientValidation === false || this.form.isValid()){
9454             
9455             if (this.form.progressUrl) {
9456                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9457                     (new Date() * 1) + '' + Math.random());
9458                     
9459             } 
9460             
9461             
9462             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9463                 form:this.form.el.dom,
9464                 url:this.getUrl(!isPost),
9465                 method: method,
9466                 params:isPost ? this.getParams() : null,
9467                 isUpload: this.form.fileUpload,
9468                 formData : this.form.formData
9469             }));
9470             
9471             this.uploadProgress();
9472
9473         }else if (o.clientValidation !== false){ // client validation failed
9474             this.failureType = Roo.form.Action.CLIENT_INVALID;
9475             this.form.afterAction(this, false);
9476         }
9477     },
9478
9479     success : function(response)
9480     {
9481         this.uploadComplete= true;
9482         if (this.haveProgress) {
9483             Roo.MessageBox.hide();
9484         }
9485         
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || result.success){
9489             this.form.afterAction(this, true);
9490             return;
9491         }
9492         if(result.errors){
9493             this.form.markInvalid(result.errors);
9494             this.failureType = Roo.form.Action.SERVER_INVALID;
9495         }
9496         this.form.afterAction(this, false);
9497     },
9498     failure : function(response)
9499     {
9500         this.uploadComplete= true;
9501         if (this.haveProgress) {
9502             Roo.MessageBox.hide();
9503         }
9504         
9505         this.response = response;
9506         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9507         this.form.afterAction(this, false);
9508     },
9509     
9510     handleResponse : function(response){
9511         if(this.form.errorReader){
9512             var rs = this.form.errorReader.read(response);
9513             var errors = [];
9514             if(rs.records){
9515                 for(var i = 0, len = rs.records.length; i < len; i++) {
9516                     var r = rs.records[i];
9517                     errors[i] = r.data;
9518                 }
9519             }
9520             if(errors.length < 1){
9521                 errors = null;
9522             }
9523             return {
9524                 success : rs.success,
9525                 errors : errors
9526             };
9527         }
9528         var ret = false;
9529         try {
9530             ret = Roo.decode(response.responseText);
9531         } catch (e) {
9532             ret = {
9533                 success: false,
9534                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9535                 errors : []
9536             };
9537         }
9538         return ret;
9539         
9540     }
9541 });
9542
9543
9544 Roo.form.Action.Load = function(form, options){
9545     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9546     this.reader = this.form.reader;
9547 };
9548
9549 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9550     type : 'load',
9551
9552     run : function(){
9553         
9554         Roo.Ajax.request(Roo.apply(
9555                 this.createCallback(), {
9556                     method:this.getMethod(),
9557                     url:this.getUrl(false),
9558                     params:this.getParams()
9559         }));
9560     },
9561
9562     success : function(response){
9563         
9564         var result = this.processResponse(response);
9565         if(result === true || !result.success || !result.data){
9566             this.failureType = Roo.form.Action.LOAD_FAILURE;
9567             this.form.afterAction(this, false);
9568             return;
9569         }
9570         this.form.clearInvalid();
9571         this.form.setValues(result.data);
9572         this.form.afterAction(this, true);
9573     },
9574
9575     handleResponse : function(response){
9576         if(this.form.reader){
9577             var rs = this.form.reader.read(response);
9578             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9579             return {
9580                 success : rs.success,
9581                 data : data
9582             };
9583         }
9584         return Roo.decode(response.responseText);
9585     }
9586 });
9587
9588 Roo.form.Action.ACTION_TYPES = {
9589     'load' : Roo.form.Action.Load,
9590     'submit' : Roo.form.Action.Submit
9591 };/*
9592  * - LGPL
9593  *
9594  * form
9595  *
9596  */
9597
9598 /**
9599  * @class Roo.bootstrap.Form
9600  * @extends Roo.bootstrap.Component
9601  * Bootstrap Form class
9602  * @cfg {String} method  GET | POST (default POST)
9603  * @cfg {String} labelAlign top | left (default top)
9604  * @cfg {String} align left  | right - for navbars
9605  * @cfg {Boolean} loadMask load mask when submit (default true)
9606
9607  *
9608  * @constructor
9609  * Create a new Form
9610  * @param {Object} config The config object
9611  */
9612
9613
9614 Roo.bootstrap.Form = function(config){
9615     
9616     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9617     
9618     Roo.bootstrap.Form.popover.apply();
9619     
9620     this.addEvents({
9621         /**
9622          * @event clientvalidation
9623          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9624          * @param {Form} this
9625          * @param {Boolean} valid true if the form has passed client-side validation
9626          */
9627         clientvalidation: true,
9628         /**
9629          * @event beforeaction
9630          * Fires before any action is performed. Return false to cancel the action.
9631          * @param {Form} this
9632          * @param {Action} action The action to be performed
9633          */
9634         beforeaction: true,
9635         /**
9636          * @event actionfailed
9637          * Fires when an action fails.
9638          * @param {Form} this
9639          * @param {Action} action The action that failed
9640          */
9641         actionfailed : true,
9642         /**
9643          * @event actioncomplete
9644          * Fires when an action is completed.
9645          * @param {Form} this
9646          * @param {Action} action The action that completed
9647          */
9648         actioncomplete : true
9649     });
9650 };
9651
9652 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9653
9654      /**
9655      * @cfg {String} method
9656      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9657      */
9658     method : 'POST',
9659     /**
9660      * @cfg {String} url
9661      * The URL to use for form actions if one isn't supplied in the action options.
9662      */
9663     /**
9664      * @cfg {Boolean} fileUpload
9665      * Set to true if this form is a file upload.
9666      */
9667
9668     /**
9669      * @cfg {Object} baseParams
9670      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9671      */
9672
9673     /**
9674      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9675      */
9676     timeout: 30,
9677     /**
9678      * @cfg {Sting} align (left|right) for navbar forms
9679      */
9680     align : 'left',
9681
9682     // private
9683     activeAction : null,
9684
9685     /**
9686      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9687      * element by passing it or its id or mask the form itself by passing in true.
9688      * @type Mixed
9689      */
9690     waitMsgTarget : false,
9691
9692     loadMask : true,
9693     
9694     /**
9695      * @cfg {Boolean} errorMask (true|false) default false
9696      */
9697     errorMask : false,
9698     
9699     /**
9700      * @cfg {Number} maskOffset Default 100
9701      */
9702     maskOffset : 100,
9703     
9704     /**
9705      * @cfg {Boolean} maskBody
9706      */
9707     maskBody : false,
9708
9709     getAutoCreate : function(){
9710
9711         var cfg = {
9712             tag: 'form',
9713             method : this.method || 'POST',
9714             id : this.id || Roo.id(),
9715             cls : ''
9716         };
9717         if (this.parent().xtype.match(/^Nav/)) {
9718             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9719
9720         }
9721
9722         if (this.labelAlign == 'left' ) {
9723             cfg.cls += ' form-horizontal';
9724         }
9725
9726
9727         return cfg;
9728     },
9729     initEvents : function()
9730     {
9731         this.el.on('submit', this.onSubmit, this);
9732         // this was added as random key presses on the form where triggering form submit.
9733         this.el.on('keypress', function(e) {
9734             if (e.getCharCode() != 13) {
9735                 return true;
9736             }
9737             // we might need to allow it for textareas.. and some other items.
9738             // check e.getTarget().
9739
9740             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9741                 return true;
9742             }
9743
9744             Roo.log("keypress blocked");
9745
9746             e.preventDefault();
9747             return false;
9748         });
9749         
9750     },
9751     // private
9752     onSubmit : function(e){
9753         e.stopEvent();
9754     },
9755
9756      /**
9757      * Returns true if client-side validation on the form is successful.
9758      * @return Boolean
9759      */
9760     isValid : function(){
9761         var items = this.getItems();
9762         var valid = true;
9763         var target = false;
9764         
9765         items.each(function(f){
9766             
9767             if(f.validate()){
9768                 return;
9769             }
9770             
9771             Roo.log('invalid field: ' + f.name);
9772             
9773             valid = false;
9774
9775             if(!target && f.el.isVisible(true)){
9776                 target = f;
9777             }
9778            
9779         });
9780         
9781         if(this.errorMask && !valid){
9782             Roo.bootstrap.Form.popover.mask(this, target);
9783         }
9784         
9785         return valid;
9786     },
9787     
9788     /**
9789      * Returns true if any fields in this form have changed since their original load.
9790      * @return Boolean
9791      */
9792     isDirty : function(){
9793         var dirty = false;
9794         var items = this.getItems();
9795         items.each(function(f){
9796            if(f.isDirty()){
9797                dirty = true;
9798                return false;
9799            }
9800            return true;
9801         });
9802         return dirty;
9803     },
9804      /**
9805      * Performs a predefined action (submit or load) or custom actions you define on this form.
9806      * @param {String} actionName The name of the action type
9807      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9808      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9809      * accept other config options):
9810      * <pre>
9811 Property          Type             Description
9812 ----------------  ---------------  ----------------------------------------------------------------------------------
9813 url               String           The url for the action (defaults to the form's url)
9814 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9815 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9816 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9817                                    validate the form on the client (defaults to false)
9818      * </pre>
9819      * @return {BasicForm} this
9820      */
9821     doAction : function(action, options){
9822         if(typeof action == 'string'){
9823             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9824         }
9825         if(this.fireEvent('beforeaction', this, action) !== false){
9826             this.beforeAction(action);
9827             action.run.defer(100, action);
9828         }
9829         return this;
9830     },
9831
9832     // private
9833     beforeAction : function(action){
9834         var o = action.options;
9835         
9836         if(this.loadMask){
9837             
9838             if(this.maskBody){
9839                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9840             } else {
9841                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9842             }
9843         }
9844         // not really supported yet.. ??
9845
9846         //if(this.waitMsgTarget === true){
9847         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9848         //}else if(this.waitMsgTarget){
9849         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9850         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9851         //}else {
9852         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9853        // }
9854
9855     },
9856
9857     // private
9858     afterAction : function(action, success){
9859         this.activeAction = null;
9860         var o = action.options;
9861
9862         if(this.loadMask){
9863             
9864             if(this.maskBody){
9865                 Roo.get(document.body).unmask();
9866             } else {
9867                 this.el.unmask();
9868             }
9869         }
9870         
9871         //if(this.waitMsgTarget === true){
9872 //            this.el.unmask();
9873         //}else if(this.waitMsgTarget){
9874         //    this.waitMsgTarget.unmask();
9875         //}else{
9876         //    Roo.MessageBox.updateProgress(1);
9877         //    Roo.MessageBox.hide();
9878        // }
9879         //
9880         if(success){
9881             if(o.reset){
9882                 this.reset();
9883             }
9884             Roo.callback(o.success, o.scope, [this, action]);
9885             this.fireEvent('actioncomplete', this, action);
9886
9887         }else{
9888
9889             // failure condition..
9890             // we have a scenario where updates need confirming.
9891             // eg. if a locking scenario exists..
9892             // we look for { errors : { needs_confirm : true }} in the response.
9893             if (
9894                 (typeof(action.result) != 'undefined')  &&
9895                 (typeof(action.result.errors) != 'undefined')  &&
9896                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9897            ){
9898                 var _t = this;
9899                 Roo.log("not supported yet");
9900                  /*
9901
9902                 Roo.MessageBox.confirm(
9903                     "Change requires confirmation",
9904                     action.result.errorMsg,
9905                     function(r) {
9906                         if (r != 'yes') {
9907                             return;
9908                         }
9909                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9910                     }
9911
9912                 );
9913                 */
9914
9915
9916                 return;
9917             }
9918
9919             Roo.callback(o.failure, o.scope, [this, action]);
9920             // show an error message if no failed handler is set..
9921             if (!this.hasListener('actionfailed')) {
9922                 Roo.log("need to add dialog support");
9923                 /*
9924                 Roo.MessageBox.alert("Error",
9925                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9926                         action.result.errorMsg :
9927                         "Saving Failed, please check your entries or try again"
9928                 );
9929                 */
9930             }
9931
9932             this.fireEvent('actionfailed', this, action);
9933         }
9934
9935     },
9936     /**
9937      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9938      * @param {String} id The value to search for
9939      * @return Field
9940      */
9941     findField : function(id){
9942         var items = this.getItems();
9943         var field = items.get(id);
9944         if(!field){
9945              items.each(function(f){
9946                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9947                     field = f;
9948                     return false;
9949                 }
9950                 return true;
9951             });
9952         }
9953         return field || null;
9954     },
9955      /**
9956      * Mark fields in this form invalid in bulk.
9957      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9958      * @return {BasicForm} this
9959      */
9960     markInvalid : function(errors){
9961         if(errors instanceof Array){
9962             for(var i = 0, len = errors.length; i < len; i++){
9963                 var fieldError = errors[i];
9964                 var f = this.findField(fieldError.id);
9965                 if(f){
9966                     f.markInvalid(fieldError.msg);
9967                 }
9968             }
9969         }else{
9970             var field, id;
9971             for(id in errors){
9972                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9973                     field.markInvalid(errors[id]);
9974                 }
9975             }
9976         }
9977         //Roo.each(this.childForms || [], function (f) {
9978         //    f.markInvalid(errors);
9979         //});
9980
9981         return this;
9982     },
9983
9984     /**
9985      * Set values for fields in this form in bulk.
9986      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9987      * @return {BasicForm} this
9988      */
9989     setValues : function(values){
9990         if(values instanceof Array){ // array of objects
9991             for(var i = 0, len = values.length; i < len; i++){
9992                 var v = values[i];
9993                 var f = this.findField(v.id);
9994                 if(f){
9995                     f.setValue(v.value);
9996                     if(this.trackResetOnLoad){
9997                         f.originalValue = f.getValue();
9998                     }
9999                 }
10000             }
10001         }else{ // object hash
10002             var field, id;
10003             for(id in values){
10004                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10005
10006                     if (field.setFromData &&
10007                         field.valueField &&
10008                         field.displayField &&
10009                         // combos' with local stores can
10010                         // be queried via setValue()
10011                         // to set their value..
10012                         (field.store && !field.store.isLocal)
10013                         ) {
10014                         // it's a combo
10015                         var sd = { };
10016                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10017                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10018                         field.setFromData(sd);
10019
10020                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10021                         
10022                         field.setFromData(values);
10023                         
10024                     } else {
10025                         field.setValue(values[id]);
10026                     }
10027
10028
10029                     if(this.trackResetOnLoad){
10030                         field.originalValue = field.getValue();
10031                     }
10032                 }
10033             }
10034         }
10035
10036         //Roo.each(this.childForms || [], function (f) {
10037         //    f.setValues(values);
10038         //});
10039
10040         return this;
10041     },
10042
10043     /**
10044      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10045      * they are returned as an array.
10046      * @param {Boolean} asString
10047      * @return {Object}
10048      */
10049     getValues : function(asString){
10050         //if (this.childForms) {
10051             // copy values from the child forms
10052         //    Roo.each(this.childForms, function (f) {
10053         //        this.setValues(f.getValues());
10054         //    }, this);
10055         //}
10056
10057
10058
10059         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10060         if(asString === true){
10061             return fs;
10062         }
10063         return Roo.urlDecode(fs);
10064     },
10065
10066     /**
10067      * Returns the fields in this form as an object with key/value pairs.
10068      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10069      * @return {Object}
10070      */
10071     getFieldValues : function(with_hidden)
10072     {
10073         var items = this.getItems();
10074         var ret = {};
10075         items.each(function(f){
10076             
10077             if (!f.getName()) {
10078                 return;
10079             }
10080             
10081             var v = f.getValue();
10082             
10083             if (f.inputType =='radio') {
10084                 if (typeof(ret[f.getName()]) == 'undefined') {
10085                     ret[f.getName()] = ''; // empty..
10086                 }
10087
10088                 if (!f.el.dom.checked) {
10089                     return;
10090
10091                 }
10092                 v = f.el.dom.value;
10093
10094             }
10095             
10096             if(f.xtype == 'MoneyField'){
10097                 ret[f.currencyName] = f.getCurrency();
10098             }
10099
10100             // not sure if this supported any more..
10101             if ((typeof(v) == 'object') && f.getRawValue) {
10102                 v = f.getRawValue() ; // dates..
10103             }
10104             // combo boxes where name != hiddenName...
10105             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10106                 ret[f.name] = f.getRawValue();
10107             }
10108             ret[f.getName()] = v;
10109         });
10110
10111         return ret;
10112     },
10113
10114     /**
10115      * Clears all invalid messages in this form.
10116      * @return {BasicForm} this
10117      */
10118     clearInvalid : function(){
10119         var items = this.getItems();
10120
10121         items.each(function(f){
10122            f.clearInvalid();
10123         });
10124
10125         return this;
10126     },
10127
10128     /**
10129      * Resets this form.
10130      * @return {BasicForm} this
10131      */
10132     reset : function(){
10133         var items = this.getItems();
10134         items.each(function(f){
10135             f.reset();
10136         });
10137
10138         Roo.each(this.childForms || [], function (f) {
10139             f.reset();
10140         });
10141
10142
10143         return this;
10144     },
10145     
10146     getItems : function()
10147     {
10148         var r=new Roo.util.MixedCollection(false, function(o){
10149             return o.id || (o.id = Roo.id());
10150         });
10151         var iter = function(el) {
10152             if (el.inputEl) {
10153                 r.add(el);
10154             }
10155             if (!el.items) {
10156                 return;
10157             }
10158             Roo.each(el.items,function(e) {
10159                 iter(e);
10160             });
10161         };
10162
10163         iter(this);
10164         return r;
10165     },
10166     
10167     hideFields : function(items)
10168     {
10169         Roo.each(items, function(i){
10170             
10171             var f = this.findField(i);
10172             
10173             if(!f){
10174                 return;
10175             }
10176             
10177             f.hide();
10178             
10179         }, this);
10180     },
10181     
10182     showFields : function(items)
10183     {
10184         Roo.each(items, function(i){
10185             
10186             var f = this.findField(i);
10187             
10188             if(!f){
10189                 return;
10190             }
10191             
10192             f.show();
10193             
10194         }, this);
10195     }
10196
10197 });
10198
10199 Roo.apply(Roo.bootstrap.Form, {
10200     
10201     popover : {
10202         
10203         padding : 5,
10204         
10205         isApplied : false,
10206         
10207         isMasked : false,
10208         
10209         form : false,
10210         
10211         target : false,
10212         
10213         toolTip : false,
10214         
10215         intervalID : false,
10216         
10217         maskEl : false,
10218         
10219         apply : function()
10220         {
10221             if(this.isApplied){
10222                 return;
10223             }
10224             
10225             this.maskEl = {
10226                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10227                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10228                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10229                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10230             };
10231             
10232             this.maskEl.top.enableDisplayMode("block");
10233             this.maskEl.left.enableDisplayMode("block");
10234             this.maskEl.bottom.enableDisplayMode("block");
10235             this.maskEl.right.enableDisplayMode("block");
10236             
10237             this.toolTip = new Roo.bootstrap.Tooltip({
10238                 cls : 'roo-form-error-popover',
10239                 alignment : {
10240                     'left' : ['r-l', [-2,0], 'right'],
10241                     'right' : ['l-r', [2,0], 'left'],
10242                     'bottom' : ['tl-bl', [0,2], 'top'],
10243                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10244                 }
10245             });
10246             
10247             this.toolTip.render(Roo.get(document.body));
10248
10249             this.toolTip.el.enableDisplayMode("block");
10250             
10251             Roo.get(document.body).on('click', function(){
10252                 this.unmask();
10253             }, this);
10254             
10255             Roo.get(document.body).on('touchstart', function(){
10256                 this.unmask();
10257             }, this);
10258             
10259             this.isApplied = true
10260         },
10261         
10262         mask : function(form, target)
10263         {
10264             this.form = form;
10265             
10266             this.target = target;
10267             
10268             if(!this.form.errorMask || !target.el){
10269                 return;
10270             }
10271             
10272             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10273             
10274             Roo.log(scrollable);
10275             
10276             var ot = this.target.el.calcOffsetsTo(scrollable);
10277             
10278             var scrollTo = ot[1] - this.form.maskOffset;
10279             
10280             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10281             
10282             scrollable.scrollTo('top', scrollTo);
10283             
10284             var box = this.target.el.getBox();
10285             Roo.log(box);
10286             var zIndex = Roo.bootstrap.Modal.zIndex++;
10287
10288             
10289             this.maskEl.top.setStyle('position', 'absolute');
10290             this.maskEl.top.setStyle('z-index', zIndex);
10291             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10292             this.maskEl.top.setLeft(0);
10293             this.maskEl.top.setTop(0);
10294             this.maskEl.top.show();
10295             
10296             this.maskEl.left.setStyle('position', 'absolute');
10297             this.maskEl.left.setStyle('z-index', zIndex);
10298             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10299             this.maskEl.left.setLeft(0);
10300             this.maskEl.left.setTop(box.y - this.padding);
10301             this.maskEl.left.show();
10302
10303             this.maskEl.bottom.setStyle('position', 'absolute');
10304             this.maskEl.bottom.setStyle('z-index', zIndex);
10305             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10306             this.maskEl.bottom.setLeft(0);
10307             this.maskEl.bottom.setTop(box.bottom + this.padding);
10308             this.maskEl.bottom.show();
10309
10310             this.maskEl.right.setStyle('position', 'absolute');
10311             this.maskEl.right.setStyle('z-index', zIndex);
10312             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10313             this.maskEl.right.setLeft(box.right + this.padding);
10314             this.maskEl.right.setTop(box.y - this.padding);
10315             this.maskEl.right.show();
10316
10317             this.toolTip.bindEl = this.target.el;
10318
10319             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10320
10321             var tip = this.target.blankText;
10322
10323             if(this.target.getValue() !== '' ) {
10324                 
10325                 if (this.target.invalidText.length) {
10326                     tip = this.target.invalidText;
10327                 } else if (this.target.regexText.length){
10328                     tip = this.target.regexText;
10329                 }
10330             }
10331
10332             this.toolTip.show(tip);
10333
10334             this.intervalID = window.setInterval(function() {
10335                 Roo.bootstrap.Form.popover.unmask();
10336             }, 10000);
10337
10338             window.onwheel = function(){ return false;};
10339             
10340             (function(){ this.isMasked = true; }).defer(500, this);
10341             
10342         },
10343         
10344         unmask : function()
10345         {
10346             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10347                 return;
10348             }
10349             
10350             this.maskEl.top.setStyle('position', 'absolute');
10351             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10352             this.maskEl.top.hide();
10353
10354             this.maskEl.left.setStyle('position', 'absolute');
10355             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10356             this.maskEl.left.hide();
10357
10358             this.maskEl.bottom.setStyle('position', 'absolute');
10359             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10360             this.maskEl.bottom.hide();
10361
10362             this.maskEl.right.setStyle('position', 'absolute');
10363             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10364             this.maskEl.right.hide();
10365             
10366             this.toolTip.hide();
10367             
10368             this.toolTip.el.hide();
10369             
10370             window.onwheel = function(){ return true;};
10371             
10372             if(this.intervalID){
10373                 window.clearInterval(this.intervalID);
10374                 this.intervalID = false;
10375             }
10376             
10377             this.isMasked = false;
10378             
10379         }
10380         
10381     }
10382     
10383 });
10384
10385 /*
10386  * Based on:
10387  * Ext JS Library 1.1.1
10388  * Copyright(c) 2006-2007, Ext JS, LLC.
10389  *
10390  * Originally Released Under LGPL - original licence link has changed is not relivant.
10391  *
10392  * Fork - LGPL
10393  * <script type="text/javascript">
10394  */
10395 /**
10396  * @class Roo.form.VTypes
10397  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10398  * @singleton
10399  */
10400 Roo.form.VTypes = function(){
10401     // closure these in so they are only created once.
10402     var alpha = /^[a-zA-Z_]+$/;
10403     var alphanum = /^[a-zA-Z0-9_]+$/;
10404     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10405     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10406
10407     // All these messages and functions are configurable
10408     return {
10409         /**
10410          * The function used to validate email addresses
10411          * @param {String} value The email address
10412          */
10413         'email' : function(v){
10414             return email.test(v);
10415         },
10416         /**
10417          * The error text to display when the email validation function returns false
10418          * @type String
10419          */
10420         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10421         /**
10422          * The keystroke filter mask to be applied on email input
10423          * @type RegExp
10424          */
10425         'emailMask' : /[a-z0-9_\.\-@]/i,
10426
10427         /**
10428          * The function used to validate URLs
10429          * @param {String} value The URL
10430          */
10431         'url' : function(v){
10432             return url.test(v);
10433         },
10434         /**
10435          * The error text to display when the url validation function returns false
10436          * @type String
10437          */
10438         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10439         
10440         /**
10441          * The function used to validate alpha values
10442          * @param {String} value The value
10443          */
10444         'alpha' : function(v){
10445             return alpha.test(v);
10446         },
10447         /**
10448          * The error text to display when the alpha validation function returns false
10449          * @type String
10450          */
10451         'alphaText' : 'This field should only contain letters and _',
10452         /**
10453          * The keystroke filter mask to be applied on alpha input
10454          * @type RegExp
10455          */
10456         'alphaMask' : /[a-z_]/i,
10457
10458         /**
10459          * The function used to validate alphanumeric values
10460          * @param {String} value The value
10461          */
10462         'alphanum' : function(v){
10463             return alphanum.test(v);
10464         },
10465         /**
10466          * The error text to display when the alphanumeric validation function returns false
10467          * @type String
10468          */
10469         'alphanumText' : 'This field should only contain letters, numbers and _',
10470         /**
10471          * The keystroke filter mask to be applied on alphanumeric input
10472          * @type RegExp
10473          */
10474         'alphanumMask' : /[a-z0-9_]/i
10475     };
10476 }();/*
10477  * - LGPL
10478  *
10479  * Input
10480  * 
10481  */
10482
10483 /**
10484  * @class Roo.bootstrap.Input
10485  * @extends Roo.bootstrap.Component
10486  * Bootstrap Input class
10487  * @cfg {Boolean} disabled is it disabled
10488  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10489  * @cfg {String} name name of the input
10490  * @cfg {string} fieldLabel - the label associated
10491  * @cfg {string} placeholder - placeholder to put in text.
10492  * @cfg {string}  before - input group add on before
10493  * @cfg {string} after - input group add on after
10494  * @cfg {string} size - (lg|sm) or leave empty..
10495  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10496  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10497  * @cfg {Number} md colspan out of 12 for computer-sized screens
10498  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10499  * @cfg {string} value default value of the input
10500  * @cfg {Number} labelWidth set the width of label 
10501  * @cfg {Number} labellg set the width of label (1-12)
10502  * @cfg {Number} labelmd set the width of label (1-12)
10503  * @cfg {Number} labelsm set the width of label (1-12)
10504  * @cfg {Number} labelxs set the width of label (1-12)
10505  * @cfg {String} labelAlign (top|left)
10506  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10507  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10508  * @cfg {String} indicatorpos (left|right) default left
10509  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10510  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10511  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10512
10513  * @cfg {String} align (left|center|right) Default left
10514  * @cfg {Boolean} forceFeedback (true|false) Default false
10515  * 
10516  * @constructor
10517  * Create a new Input
10518  * @param {Object} config The config object
10519  */
10520
10521 Roo.bootstrap.Input = function(config){
10522     
10523     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10524     
10525     this.addEvents({
10526         /**
10527          * @event focus
10528          * Fires when this field receives input focus.
10529          * @param {Roo.form.Field} this
10530          */
10531         focus : true,
10532         /**
10533          * @event blur
10534          * Fires when this field loses input focus.
10535          * @param {Roo.form.Field} this
10536          */
10537         blur : true,
10538         /**
10539          * @event specialkey
10540          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10541          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10542          * @param {Roo.form.Field} this
10543          * @param {Roo.EventObject} e The event object
10544          */
10545         specialkey : true,
10546         /**
10547          * @event change
10548          * Fires just before the field blurs if the field value has changed.
10549          * @param {Roo.form.Field} this
10550          * @param {Mixed} newValue The new value
10551          * @param {Mixed} oldValue The original value
10552          */
10553         change : true,
10554         /**
10555          * @event invalid
10556          * Fires after the field has been marked as invalid.
10557          * @param {Roo.form.Field} this
10558          * @param {String} msg The validation message
10559          */
10560         invalid : true,
10561         /**
10562          * @event valid
10563          * Fires after the field has been validated with no errors.
10564          * @param {Roo.form.Field} this
10565          */
10566         valid : true,
10567          /**
10568          * @event keyup
10569          * Fires after the key up
10570          * @param {Roo.form.Field} this
10571          * @param {Roo.EventObject}  e The event Object
10572          */
10573         keyup : true,
10574         /**
10575          * @event paste
10576          * Fires after the user pastes into input
10577          * @param {Roo.form.Field} this
10578          * @param {Roo.EventObject}  e The event Object
10579          */
10580         paste : true
10581     });
10582 };
10583
10584 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10585      /**
10586      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10587       automatic validation (defaults to "keyup").
10588      */
10589     validationEvent : "keyup",
10590      /**
10591      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10592      */
10593     validateOnBlur : true,
10594     /**
10595      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10596      */
10597     validationDelay : 250,
10598      /**
10599      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10600      */
10601     focusClass : "x-form-focus",  // not needed???
10602     
10603        
10604     /**
10605      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10606      */
10607     invalidClass : "has-warning",
10608     
10609     /**
10610      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10611      */
10612     validClass : "has-success",
10613     
10614     /**
10615      * @cfg {Boolean} hasFeedback (true|false) default true
10616      */
10617     hasFeedback : true,
10618     
10619     /**
10620      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10621      */
10622     invalidFeedbackClass : "glyphicon-warning-sign",
10623     
10624     /**
10625      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10626      */
10627     validFeedbackClass : "glyphicon-ok",
10628     
10629     /**
10630      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10631      */
10632     selectOnFocus : false,
10633     
10634      /**
10635      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10636      */
10637     maskRe : null,
10638        /**
10639      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10640      */
10641     vtype : null,
10642     
10643       /**
10644      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10645      */
10646     disableKeyFilter : false,
10647     
10648        /**
10649      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10650      */
10651     disabled : false,
10652      /**
10653      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10654      */
10655     allowBlank : true,
10656     /**
10657      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10658      */
10659     blankText : "Please complete this mandatory field",
10660     
10661      /**
10662      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10663      */
10664     minLength : 0,
10665     /**
10666      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10667      */
10668     maxLength : Number.MAX_VALUE,
10669     /**
10670      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10671      */
10672     minLengthText : "The minimum length for this field is {0}",
10673     /**
10674      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10675      */
10676     maxLengthText : "The maximum length for this field is {0}",
10677   
10678     
10679     /**
10680      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10681      * If available, this function will be called only after the basic validators all return true, and will be passed the
10682      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10683      */
10684     validator : null,
10685     /**
10686      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10687      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10688      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10689      */
10690     regex : null,
10691     /**
10692      * @cfg {String} regexText -- Depricated - use Invalid Text
10693      */
10694     regexText : "",
10695     
10696     /**
10697      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10698      */
10699     invalidText : "",
10700     
10701     
10702     
10703     autocomplete: false,
10704     
10705     
10706     fieldLabel : '',
10707     inputType : 'text',
10708     
10709     name : false,
10710     placeholder: false,
10711     before : false,
10712     after : false,
10713     size : false,
10714     hasFocus : false,
10715     preventMark: false,
10716     isFormField : true,
10717     value : '',
10718     labelWidth : 2,
10719     labelAlign : false,
10720     readOnly : false,
10721     align : false,
10722     formatedValue : false,
10723     forceFeedback : false,
10724     
10725     indicatorpos : 'left',
10726     
10727     labellg : 0,
10728     labelmd : 0,
10729     labelsm : 0,
10730     labelxs : 0,
10731     
10732     capture : '',
10733     accept : '',
10734     
10735     parentLabelAlign : function()
10736     {
10737         var parent = this;
10738         while (parent.parent()) {
10739             parent = parent.parent();
10740             if (typeof(parent.labelAlign) !='undefined') {
10741                 return parent.labelAlign;
10742             }
10743         }
10744         return 'left';
10745         
10746     },
10747     
10748     getAutoCreate : function()
10749     {
10750         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10751         
10752         var id = Roo.id();
10753         
10754         var cfg = {};
10755         
10756         if(this.inputType != 'hidden'){
10757             cfg.cls = 'form-group' //input-group
10758         }
10759         
10760         var input =  {
10761             tag: 'input',
10762             id : id,
10763             type : this.inputType,
10764             value : this.value,
10765             cls : 'form-control',
10766             placeholder : this.placeholder || '',
10767             autocomplete : this.autocomplete || 'new-password'
10768         };
10769         if (this.inputType == 'file') {
10770             input.style = 'overflow:hidden'; // why not in CSS?
10771         }
10772         
10773         if(this.capture.length){
10774             input.capture = this.capture;
10775         }
10776         
10777         if(this.accept.length){
10778             input.accept = this.accept + "/*";
10779         }
10780         
10781         if(this.align){
10782             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10783         }
10784         
10785         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10786             input.maxLength = this.maxLength;
10787         }
10788         
10789         if (this.disabled) {
10790             input.disabled=true;
10791         }
10792         
10793         if (this.readOnly) {
10794             input.readonly=true;
10795         }
10796         
10797         if (this.name) {
10798             input.name = this.name;
10799         }
10800         
10801         if (this.size) {
10802             input.cls += ' input-' + this.size;
10803         }
10804         
10805         var settings=this;
10806         ['xs','sm','md','lg'].map(function(size){
10807             if (settings[size]) {
10808                 cfg.cls += ' col-' + size + '-' + settings[size];
10809             }
10810         });
10811         
10812         var inputblock = input;
10813         
10814         var feedback = {
10815             tag: 'span',
10816             cls: 'glyphicon form-control-feedback'
10817         };
10818             
10819         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10820             
10821             inputblock = {
10822                 cls : 'has-feedback',
10823                 cn :  [
10824                     input,
10825                     feedback
10826                 ] 
10827             };  
10828         }
10829         
10830         if (this.before || this.after) {
10831             
10832             inputblock = {
10833                 cls : 'input-group',
10834                 cn :  [] 
10835             };
10836             
10837             if (this.before && typeof(this.before) == 'string') {
10838                 
10839                 inputblock.cn.push({
10840                     tag :'span',
10841                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10842                     html : this.before
10843                 });
10844             }
10845             if (this.before && typeof(this.before) == 'object') {
10846                 this.before = Roo.factory(this.before);
10847                 
10848                 inputblock.cn.push({
10849                     tag :'span',
10850                     cls : 'roo-input-before input-group-prepend   input-group-' +
10851                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10852                 });
10853             }
10854             
10855             inputblock.cn.push(input);
10856             
10857             if (this.after && typeof(this.after) == 'string') {
10858                 inputblock.cn.push({
10859                     tag :'span',
10860                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10861                     html : this.after
10862                 });
10863             }
10864             if (this.after && typeof(this.after) == 'object') {
10865                 this.after = Roo.factory(this.after);
10866                 
10867                 inputblock.cn.push({
10868                     tag :'span',
10869                     cls : 'roo-input-after input-group-append  input-group-' +
10870                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10871                 });
10872             }
10873             
10874             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10875                 inputblock.cls += ' has-feedback';
10876                 inputblock.cn.push(feedback);
10877             }
10878         };
10879         var indicator = {
10880             tag : 'i',
10881             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10882             tooltip : 'This field is required'
10883         };
10884         if (this.allowBlank ) {
10885             indicator.style = this.allowBlank ? ' display:none' : '';
10886         }
10887         if (align ==='left' && this.fieldLabel.length) {
10888             
10889             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10890             
10891             cfg.cn = [
10892                 indicator,
10893                 {
10894                     tag: 'label',
10895                     'for' :  id,
10896                     cls : 'control-label col-form-label',
10897                     html : this.fieldLabel
10898
10899                 },
10900                 {
10901                     cls : "", 
10902                     cn: [
10903                         inputblock
10904                     ]
10905                 }
10906             ];
10907             
10908             var labelCfg = cfg.cn[1];
10909             var contentCfg = cfg.cn[2];
10910             
10911             if(this.indicatorpos == 'right'){
10912                 cfg.cn = [
10913                     {
10914                         tag: 'label',
10915                         'for' :  id,
10916                         cls : 'control-label col-form-label',
10917                         cn : [
10918                             {
10919                                 tag : 'span',
10920                                 html : this.fieldLabel
10921                             },
10922                             indicator
10923                         ]
10924                     },
10925                     {
10926                         cls : "",
10927                         cn: [
10928                             inputblock
10929                         ]
10930                     }
10931
10932                 ];
10933                 
10934                 labelCfg = cfg.cn[0];
10935                 contentCfg = cfg.cn[1];
10936             
10937             }
10938             
10939             if(this.labelWidth > 12){
10940                 labelCfg.style = "width: " + this.labelWidth + 'px';
10941             }
10942             
10943             if(this.labelWidth < 13 && this.labelmd == 0){
10944                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
10945             }
10946             
10947             if(this.labellg > 0){
10948                 labelCfg.cls += ' col-lg-' + this.labellg;
10949                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10950             }
10951             
10952             if(this.labelmd > 0){
10953                 labelCfg.cls += ' col-md-' + this.labelmd;
10954                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10955             }
10956             
10957             if(this.labelsm > 0){
10958                 labelCfg.cls += ' col-sm-' + this.labelsm;
10959                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10960             }
10961             
10962             if(this.labelxs > 0){
10963                 labelCfg.cls += ' col-xs-' + this.labelxs;
10964                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10965             }
10966             
10967             
10968         } else if ( this.fieldLabel.length) {
10969                 
10970             
10971             
10972             cfg.cn = [
10973                 {
10974                     tag : 'i',
10975                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10976                     tooltip : 'This field is required',
10977                     style : this.allowBlank ? ' display:none' : '' 
10978                 },
10979                 {
10980                     tag: 'label',
10981                    //cls : 'input-group-addon',
10982                     html : this.fieldLabel
10983
10984                 },
10985
10986                inputblock
10987
10988            ];
10989            
10990            if(this.indicatorpos == 'right'){
10991        
10992                 cfg.cn = [
10993                     {
10994                         tag: 'label',
10995                        //cls : 'input-group-addon',
10996                         html : this.fieldLabel
10997
10998                     },
10999                     {
11000                         tag : 'i',
11001                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11002                         tooltip : 'This field is required',
11003                         style : this.allowBlank ? ' display:none' : '' 
11004                     },
11005
11006                    inputblock
11007
11008                ];
11009
11010             }
11011
11012         } else {
11013             
11014             cfg.cn = [
11015
11016                     inputblock
11017
11018             ];
11019                 
11020                 
11021         };
11022         
11023         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11024            cfg.cls += ' navbar-form';
11025         }
11026         
11027         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11028             // on BS4 we do this only if not form 
11029             cfg.cls += ' navbar-form';
11030             cfg.tag = 'li';
11031         }
11032         
11033         return cfg;
11034         
11035     },
11036     /**
11037      * return the real input element.
11038      */
11039     inputEl: function ()
11040     {
11041         return this.el.select('input.form-control',true).first();
11042     },
11043     
11044     tooltipEl : function()
11045     {
11046         return this.inputEl();
11047     },
11048     
11049     indicatorEl : function()
11050     {
11051         if (Roo.bootstrap.version == 4) {
11052             return false; // not enabled in v4 yet.
11053         }
11054         
11055         var indicator = this.el.select('i.roo-required-indicator',true).first();
11056         
11057         if(!indicator){
11058             return false;
11059         }
11060         
11061         return indicator;
11062         
11063     },
11064     
11065     setDisabled : function(v)
11066     {
11067         var i  = this.inputEl().dom;
11068         if (!v) {
11069             i.removeAttribute('disabled');
11070             return;
11071             
11072         }
11073         i.setAttribute('disabled','true');
11074     },
11075     initEvents : function()
11076     {
11077           
11078         this.inputEl().on("keydown" , this.fireKey,  this);
11079         this.inputEl().on("focus", this.onFocus,  this);
11080         this.inputEl().on("blur", this.onBlur,  this);
11081         
11082         this.inputEl().relayEvent('keyup', this);
11083         this.inputEl().relayEvent('paste', this);
11084         
11085         this.indicator = this.indicatorEl();
11086         
11087         if(this.indicator){
11088             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11089         }
11090  
11091         // reference to original value for reset
11092         this.originalValue = this.getValue();
11093         //Roo.form.TextField.superclass.initEvents.call(this);
11094         if(this.validationEvent == 'keyup'){
11095             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11096             this.inputEl().on('keyup', this.filterValidation, this);
11097         }
11098         else if(this.validationEvent !== false){
11099             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11100         }
11101         
11102         if(this.selectOnFocus){
11103             this.on("focus", this.preFocus, this);
11104             
11105         }
11106         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11107             this.inputEl().on("keypress", this.filterKeys, this);
11108         } else {
11109             this.inputEl().relayEvent('keypress', this);
11110         }
11111        /* if(this.grow){
11112             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11113             this.el.on("click", this.autoSize,  this);
11114         }
11115         */
11116         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11117             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11118         }
11119         
11120         if (typeof(this.before) == 'object') {
11121             this.before.render(this.el.select('.roo-input-before',true).first());
11122         }
11123         if (typeof(this.after) == 'object') {
11124             this.after.render(this.el.select('.roo-input-after',true).first());
11125         }
11126         
11127         this.inputEl().on('change', this.onChange, this);
11128         
11129     },
11130     filterValidation : function(e){
11131         if(!e.isNavKeyPress()){
11132             this.validationTask.delay(this.validationDelay);
11133         }
11134     },
11135      /**
11136      * Validates the field value
11137      * @return {Boolean} True if the value is valid, else false
11138      */
11139     validate : function(){
11140         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11141         if(this.disabled || this.validateValue(this.getRawValue())){
11142             this.markValid();
11143             return true;
11144         }
11145         
11146         this.markInvalid();
11147         return false;
11148     },
11149     
11150     
11151     /**
11152      * Validates a value according to the field's validation rules and marks the field as invalid
11153      * if the validation fails
11154      * @param {Mixed} value The value to validate
11155      * @return {Boolean} True if the value is valid, else false
11156      */
11157     validateValue : function(value)
11158     {
11159         if(this.getVisibilityEl().hasClass('hidden')){
11160             return true;
11161         }
11162         
11163         if(value.length < 1)  { // if it's blank
11164             if(this.allowBlank){
11165                 return true;
11166             }
11167             return false;
11168         }
11169         
11170         if(value.length < this.minLength){
11171             return false;
11172         }
11173         if(value.length > this.maxLength){
11174             return false;
11175         }
11176         if(this.vtype){
11177             var vt = Roo.form.VTypes;
11178             if(!vt[this.vtype](value, this)){
11179                 return false;
11180             }
11181         }
11182         if(typeof this.validator == "function"){
11183             var msg = this.validator(value);
11184             if(msg !== true){
11185                 return false;
11186             }
11187             if (typeof(msg) == 'string') {
11188                 this.invalidText = msg;
11189             }
11190         }
11191         
11192         if(this.regex && !this.regex.test(value)){
11193             return false;
11194         }
11195         
11196         return true;
11197     },
11198     
11199      // private
11200     fireKey : function(e){
11201         //Roo.log('field ' + e.getKey());
11202         if(e.isNavKeyPress()){
11203             this.fireEvent("specialkey", this, e);
11204         }
11205     },
11206     focus : function (selectText){
11207         if(this.rendered){
11208             this.inputEl().focus();
11209             if(selectText === true){
11210                 this.inputEl().dom.select();
11211             }
11212         }
11213         return this;
11214     } ,
11215     
11216     onFocus : function(){
11217         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11218            // this.el.addClass(this.focusClass);
11219         }
11220         if(!this.hasFocus){
11221             this.hasFocus = true;
11222             this.startValue = this.getValue();
11223             this.fireEvent("focus", this);
11224         }
11225     },
11226     
11227     beforeBlur : Roo.emptyFn,
11228
11229     
11230     // private
11231     onBlur : function(){
11232         this.beforeBlur();
11233         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11234             //this.el.removeClass(this.focusClass);
11235         }
11236         this.hasFocus = false;
11237         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11238             this.validate();
11239         }
11240         var v = this.getValue();
11241         if(String(v) !== String(this.startValue)){
11242             this.fireEvent('change', this, v, this.startValue);
11243         }
11244         this.fireEvent("blur", this);
11245     },
11246     
11247     onChange : function(e)
11248     {
11249         var v = this.getValue();
11250         if(String(v) !== String(this.startValue)){
11251             this.fireEvent('change', this, v, this.startValue);
11252         }
11253         
11254     },
11255     
11256     /**
11257      * Resets the current field value to the originally loaded value and clears any validation messages
11258      */
11259     reset : function(){
11260         this.setValue(this.originalValue);
11261         this.validate();
11262     },
11263      /**
11264      * Returns the name of the field
11265      * @return {Mixed} name The name field
11266      */
11267     getName: function(){
11268         return this.name;
11269     },
11270      /**
11271      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11272      * @return {Mixed} value The field value
11273      */
11274     getValue : function(){
11275         
11276         var v = this.inputEl().getValue();
11277         
11278         return v;
11279     },
11280     /**
11281      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11282      * @return {Mixed} value The field value
11283      */
11284     getRawValue : function(){
11285         var v = this.inputEl().getValue();
11286         
11287         return v;
11288     },
11289     
11290     /**
11291      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11292      * @param {Mixed} value The value to set
11293      */
11294     setRawValue : function(v){
11295         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11296     },
11297     
11298     selectText : function(start, end){
11299         var v = this.getRawValue();
11300         if(v.length > 0){
11301             start = start === undefined ? 0 : start;
11302             end = end === undefined ? v.length : end;
11303             var d = this.inputEl().dom;
11304             if(d.setSelectionRange){
11305                 d.setSelectionRange(start, end);
11306             }else if(d.createTextRange){
11307                 var range = d.createTextRange();
11308                 range.moveStart("character", start);
11309                 range.moveEnd("character", v.length-end);
11310                 range.select();
11311             }
11312         }
11313     },
11314     
11315     /**
11316      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11317      * @param {Mixed} value The value to set
11318      */
11319     setValue : function(v){
11320         this.value = v;
11321         if(this.rendered){
11322             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11323             this.validate();
11324         }
11325     },
11326     
11327     /*
11328     processValue : function(value){
11329         if(this.stripCharsRe){
11330             var newValue = value.replace(this.stripCharsRe, '');
11331             if(newValue !== value){
11332                 this.setRawValue(newValue);
11333                 return newValue;
11334             }
11335         }
11336         return value;
11337     },
11338   */
11339     preFocus : function(){
11340         
11341         if(this.selectOnFocus){
11342             this.inputEl().dom.select();
11343         }
11344     },
11345     filterKeys : function(e){
11346         var k = e.getKey();
11347         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11348             return;
11349         }
11350         var c = e.getCharCode(), cc = String.fromCharCode(c);
11351         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11352             return;
11353         }
11354         if(!this.maskRe.test(cc)){
11355             e.stopEvent();
11356         }
11357     },
11358      /**
11359      * Clear any invalid styles/messages for this field
11360      */
11361     clearInvalid : function(){
11362         
11363         if(!this.el || this.preventMark){ // not rendered
11364             return;
11365         }
11366         
11367         
11368         this.el.removeClass([this.invalidClass, 'is-invalid']);
11369         
11370         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11371             
11372             var feedback = this.el.select('.form-control-feedback', true).first();
11373             
11374             if(feedback){
11375                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11376             }
11377             
11378         }
11379         
11380         if(this.indicator){
11381             this.indicator.removeClass('visible');
11382             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11383         }
11384         
11385         this.fireEvent('valid', this);
11386     },
11387     
11388      /**
11389      * Mark this field as valid
11390      */
11391     markValid : function()
11392     {
11393         if(!this.el  || this.preventMark){ // not rendered...
11394             return;
11395         }
11396         
11397         this.el.removeClass([this.invalidClass, this.validClass]);
11398         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11399
11400         var feedback = this.el.select('.form-control-feedback', true).first();
11401             
11402         if(feedback){
11403             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11404         }
11405         
11406         if(this.indicator){
11407             this.indicator.removeClass('visible');
11408             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11409         }
11410         
11411         if(this.disabled){
11412             return;
11413         }
11414         
11415            
11416         if(this.allowBlank && !this.getRawValue().length){
11417             return;
11418         }
11419         if (Roo.bootstrap.version == 3) {
11420             this.el.addClass(this.validClass);
11421         } else {
11422             this.inputEl().addClass('is-valid');
11423         }
11424
11425         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11426             
11427             var feedback = this.el.select('.form-control-feedback', true).first();
11428             
11429             if(feedback){
11430                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11431                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11432             }
11433             
11434         }
11435         
11436         this.fireEvent('valid', this);
11437     },
11438     
11439      /**
11440      * Mark this field as invalid
11441      * @param {String} msg The validation message
11442      */
11443     markInvalid : function(msg)
11444     {
11445         if(!this.el  || this.preventMark){ // not rendered
11446             return;
11447         }
11448         
11449         this.el.removeClass([this.invalidClass, this.validClass]);
11450         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11451         
11452         var feedback = this.el.select('.form-control-feedback', true).first();
11453             
11454         if(feedback){
11455             this.el.select('.form-control-feedback', true).first().removeClass(
11456                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11457         }
11458
11459         if(this.disabled){
11460             return;
11461         }
11462         
11463         if(this.allowBlank && !this.getRawValue().length){
11464             return;
11465         }
11466         
11467         if(this.indicator){
11468             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11469             this.indicator.addClass('visible');
11470         }
11471         if (Roo.bootstrap.version == 3) {
11472             this.el.addClass(this.invalidClass);
11473         } else {
11474             this.inputEl().addClass('is-invalid');
11475         }
11476         
11477         
11478         
11479         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11480             
11481             var feedback = this.el.select('.form-control-feedback', true).first();
11482             
11483             if(feedback){
11484                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11485                 
11486                 if(this.getValue().length || this.forceFeedback){
11487                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11488                 }
11489                 
11490             }
11491             
11492         }
11493         
11494         this.fireEvent('invalid', this, msg);
11495     },
11496     // private
11497     SafariOnKeyDown : function(event)
11498     {
11499         // this is a workaround for a password hang bug on chrome/ webkit.
11500         if (this.inputEl().dom.type != 'password') {
11501             return;
11502         }
11503         
11504         var isSelectAll = false;
11505         
11506         if(this.inputEl().dom.selectionEnd > 0){
11507             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11508         }
11509         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11510             event.preventDefault();
11511             this.setValue('');
11512             return;
11513         }
11514         
11515         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11516             
11517             event.preventDefault();
11518             // this is very hacky as keydown always get's upper case.
11519             //
11520             var cc = String.fromCharCode(event.getCharCode());
11521             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11522             
11523         }
11524     },
11525     adjustWidth : function(tag, w){
11526         tag = tag.toLowerCase();
11527         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11528             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11529                 if(tag == 'input'){
11530                     return w + 2;
11531                 }
11532                 if(tag == 'textarea'){
11533                     return w-2;
11534                 }
11535             }else if(Roo.isOpera){
11536                 if(tag == 'input'){
11537                     return w + 2;
11538                 }
11539                 if(tag == 'textarea'){
11540                     return w-2;
11541                 }
11542             }
11543         }
11544         return w;
11545     },
11546     
11547     setFieldLabel : function(v)
11548     {
11549         if(!this.rendered){
11550             return;
11551         }
11552         
11553         if(this.indicatorEl()){
11554             var ar = this.el.select('label > span',true);
11555             
11556             if (ar.elements.length) {
11557                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11558                 this.fieldLabel = v;
11559                 return;
11560             }
11561             
11562             var br = this.el.select('label',true);
11563             
11564             if(br.elements.length) {
11565                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11566                 this.fieldLabel = v;
11567                 return;
11568             }
11569             
11570             Roo.log('Cannot Found any of label > span || label in input');
11571             return;
11572         }
11573         
11574         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11575         this.fieldLabel = v;
11576         
11577         
11578     }
11579 });
11580
11581  
11582 /*
11583  * - LGPL
11584  *
11585  * Input
11586  * 
11587  */
11588
11589 /**
11590  * @class Roo.bootstrap.TextArea
11591  * @extends Roo.bootstrap.Input
11592  * Bootstrap TextArea class
11593  * @cfg {Number} cols Specifies the visible width of a text area
11594  * @cfg {Number} rows Specifies the visible number of lines in a text area
11595  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11596  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11597  * @cfg {string} html text
11598  * 
11599  * @constructor
11600  * Create a new TextArea
11601  * @param {Object} config The config object
11602  */
11603
11604 Roo.bootstrap.TextArea = function(config){
11605     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11606    
11607 };
11608
11609 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11610      
11611     cols : false,
11612     rows : 5,
11613     readOnly : false,
11614     warp : 'soft',
11615     resize : false,
11616     value: false,
11617     html: false,
11618     
11619     getAutoCreate : function(){
11620         
11621         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11622         
11623         var id = Roo.id();
11624         
11625         var cfg = {};
11626         
11627         if(this.inputType != 'hidden'){
11628             cfg.cls = 'form-group' //input-group
11629         }
11630         
11631         var input =  {
11632             tag: 'textarea',
11633             id : id,
11634             warp : this.warp,
11635             rows : this.rows,
11636             value : this.value || '',
11637             html: this.html || '',
11638             cls : 'form-control',
11639             placeholder : this.placeholder || '' 
11640             
11641         };
11642         
11643         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11644             input.maxLength = this.maxLength;
11645         }
11646         
11647         if(this.resize){
11648             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11649         }
11650         
11651         if(this.cols){
11652             input.cols = this.cols;
11653         }
11654         
11655         if (this.readOnly) {
11656             input.readonly = true;
11657         }
11658         
11659         if (this.name) {
11660             input.name = this.name;
11661         }
11662         
11663         if (this.size) {
11664             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11665         }
11666         
11667         var settings=this;
11668         ['xs','sm','md','lg'].map(function(size){
11669             if (settings[size]) {
11670                 cfg.cls += ' col-' + size + '-' + settings[size];
11671             }
11672         });
11673         
11674         var inputblock = input;
11675         
11676         if(this.hasFeedback && !this.allowBlank){
11677             
11678             var feedback = {
11679                 tag: 'span',
11680                 cls: 'glyphicon form-control-feedback'
11681             };
11682
11683             inputblock = {
11684                 cls : 'has-feedback',
11685                 cn :  [
11686                     input,
11687                     feedback
11688                 ] 
11689             };  
11690         }
11691         
11692         
11693         if (this.before || this.after) {
11694             
11695             inputblock = {
11696                 cls : 'input-group',
11697                 cn :  [] 
11698             };
11699             if (this.before) {
11700                 inputblock.cn.push({
11701                     tag :'span',
11702                     cls : 'input-group-addon',
11703                     html : this.before
11704                 });
11705             }
11706             
11707             inputblock.cn.push(input);
11708             
11709             if(this.hasFeedback && !this.allowBlank){
11710                 inputblock.cls += ' has-feedback';
11711                 inputblock.cn.push(feedback);
11712             }
11713             
11714             if (this.after) {
11715                 inputblock.cn.push({
11716                     tag :'span',
11717                     cls : 'input-group-addon',
11718                     html : this.after
11719                 });
11720             }
11721             
11722         }
11723         
11724         if (align ==='left' && this.fieldLabel.length) {
11725             cfg.cn = [
11726                 {
11727                     tag: 'label',
11728                     'for' :  id,
11729                     cls : 'control-label',
11730                     html : this.fieldLabel
11731                 },
11732                 {
11733                     cls : "",
11734                     cn: [
11735                         inputblock
11736                     ]
11737                 }
11738
11739             ];
11740             
11741             if(this.labelWidth > 12){
11742                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11743             }
11744
11745             if(this.labelWidth < 13 && this.labelmd == 0){
11746                 this.labelmd = this.labelWidth;
11747             }
11748
11749             if(this.labellg > 0){
11750                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11751                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11752             }
11753
11754             if(this.labelmd > 0){
11755                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11756                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11757             }
11758
11759             if(this.labelsm > 0){
11760                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11761                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11762             }
11763
11764             if(this.labelxs > 0){
11765                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11766                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11767             }
11768             
11769         } else if ( this.fieldLabel.length) {
11770             cfg.cn = [
11771
11772                {
11773                    tag: 'label',
11774                    //cls : 'input-group-addon',
11775                    html : this.fieldLabel
11776
11777                },
11778
11779                inputblock
11780
11781            ];
11782
11783         } else {
11784
11785             cfg.cn = [
11786
11787                 inputblock
11788
11789             ];
11790                 
11791         }
11792         
11793         if (this.disabled) {
11794             input.disabled=true;
11795         }
11796         
11797         return cfg;
11798         
11799     },
11800     /**
11801      * return the real textarea element.
11802      */
11803     inputEl: function ()
11804     {
11805         return this.el.select('textarea.form-control',true).first();
11806     },
11807     
11808     /**
11809      * Clear any invalid styles/messages for this field
11810      */
11811     clearInvalid : function()
11812     {
11813         
11814         if(!this.el || this.preventMark){ // not rendered
11815             return;
11816         }
11817         
11818         var label = this.el.select('label', true).first();
11819         var icon = this.el.select('i.fa-star', true).first();
11820         
11821         if(label && icon){
11822             icon.remove();
11823         }
11824         this.el.removeClass( this.validClass);
11825         this.inputEl().removeClass('is-invalid');
11826          
11827         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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);
11833             }
11834             
11835         }
11836         
11837         this.fireEvent('valid', this);
11838     },
11839     
11840      /**
11841      * Mark this field as valid
11842      */
11843     markValid : function()
11844     {
11845         if(!this.el  || this.preventMark){ // not rendered
11846             return;
11847         }
11848         
11849         this.el.removeClass([this.invalidClass, this.validClass]);
11850         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11851         
11852         var feedback = this.el.select('.form-control-feedback', true).first();
11853             
11854         if(feedback){
11855             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11856         }
11857
11858         if(this.disabled || this.allowBlank){
11859             return;
11860         }
11861         
11862         var label = this.el.select('label', true).first();
11863         var icon = this.el.select('i.fa-star', true).first();
11864         
11865         if(label && icon){
11866             icon.remove();
11867         }
11868         if (Roo.bootstrap.version == 3) {
11869             this.el.addClass(this.validClass);
11870         } else {
11871             this.inputEl().addClass('is-valid');
11872         }
11873         
11874         
11875         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11876             
11877             var feedback = this.el.select('.form-control-feedback', true).first();
11878             
11879             if(feedback){
11880                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11881                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11882             }
11883             
11884         }
11885         
11886         this.fireEvent('valid', this);
11887     },
11888     
11889      /**
11890      * Mark this field as invalid
11891      * @param {String} msg The validation message
11892      */
11893     markInvalid : function(msg)
11894     {
11895         if(!this.el  || this.preventMark){ // not rendered
11896             return;
11897         }
11898         
11899         this.el.removeClass([this.invalidClass, this.validClass]);
11900         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11901         
11902         var feedback = this.el.select('.form-control-feedback', true).first();
11903             
11904         if(feedback){
11905             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11906         }
11907
11908         if(this.disabled || this.allowBlank){
11909             return;
11910         }
11911         
11912         var label = this.el.select('label', true).first();
11913         var icon = this.el.select('i.fa-star', true).first();
11914         
11915         if(!this.getValue().length && label && !icon){
11916             this.el.createChild({
11917                 tag : 'i',
11918                 cls : 'text-danger fa fa-lg fa-star',
11919                 tooltip : 'This field is required',
11920                 style : 'margin-right:5px;'
11921             }, label, true);
11922         }
11923         
11924         if (Roo.bootstrap.version == 3) {
11925             this.el.addClass(this.invalidClass);
11926         } else {
11927             this.inputEl().addClass('is-invalid');
11928         }
11929         
11930         // fixme ... this may be depricated need to test..
11931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11932             
11933             var feedback = this.el.select('.form-control-feedback', true).first();
11934             
11935             if(feedback){
11936                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11937                 
11938                 if(this.getValue().length || this.forceFeedback){
11939                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11940                 }
11941                 
11942             }
11943             
11944         }
11945         
11946         this.fireEvent('invalid', this, msg);
11947     }
11948 });
11949
11950  
11951 /*
11952  * - LGPL
11953  *
11954  * trigger field - base class for combo..
11955  * 
11956  */
11957  
11958 /**
11959  * @class Roo.bootstrap.TriggerField
11960  * @extends Roo.bootstrap.Input
11961  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11962  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11963  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11964  * for which you can provide a custom implementation.  For example:
11965  * <pre><code>
11966 var trigger = new Roo.bootstrap.TriggerField();
11967 trigger.onTriggerClick = myTriggerFn;
11968 trigger.applyTo('my-field');
11969 </code></pre>
11970  *
11971  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11972  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11973  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11974  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11975  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11976
11977  * @constructor
11978  * Create a new TriggerField.
11979  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11980  * to the base TextField)
11981  */
11982 Roo.bootstrap.TriggerField = function(config){
11983     this.mimicing = false;
11984     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11985 };
11986
11987 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11988     /**
11989      * @cfg {String} triggerClass A CSS class to apply to the trigger
11990      */
11991      /**
11992      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11993      */
11994     hideTrigger:false,
11995
11996     /**
11997      * @cfg {Boolean} removable (true|false) special filter default false
11998      */
11999     removable : false,
12000     
12001     /** @cfg {Boolean} grow @hide */
12002     /** @cfg {Number} growMin @hide */
12003     /** @cfg {Number} growMax @hide */
12004
12005     /**
12006      * @hide 
12007      * @method
12008      */
12009     autoSize: Roo.emptyFn,
12010     // private
12011     monitorTab : true,
12012     // private
12013     deferHeight : true,
12014
12015     
12016     actionMode : 'wrap',
12017     
12018     caret : false,
12019     
12020     
12021     getAutoCreate : function(){
12022        
12023         var align = this.labelAlign || this.parentLabelAlign();
12024         
12025         var id = Roo.id();
12026         
12027         var cfg = {
12028             cls: 'form-group' //input-group
12029         };
12030         
12031         
12032         var input =  {
12033             tag: 'input',
12034             id : id,
12035             type : this.inputType,
12036             cls : 'form-control',
12037             autocomplete: 'new-password',
12038             placeholder : this.placeholder || '' 
12039             
12040         };
12041         if (this.name) {
12042             input.name = this.name;
12043         }
12044         if (this.size) {
12045             input.cls += ' input-' + this.size;
12046         }
12047         
12048         if (this.disabled) {
12049             input.disabled=true;
12050         }
12051         
12052         var inputblock = input;
12053         
12054         if(this.hasFeedback && !this.allowBlank){
12055             
12056             var feedback = {
12057                 tag: 'span',
12058                 cls: 'glyphicon form-control-feedback'
12059             };
12060             
12061             if(this.removable && !this.editable  ){
12062                 inputblock = {
12063                     cls : 'has-feedback',
12064                     cn :  [
12065                         inputblock,
12066                         {
12067                             tag: 'button',
12068                             html : 'x',
12069                             cls : 'roo-combo-removable-btn close'
12070                         },
12071                         feedback
12072                     ] 
12073                 };
12074             } else {
12075                 inputblock = {
12076                     cls : 'has-feedback',
12077                     cn :  [
12078                         inputblock,
12079                         feedback
12080                     ] 
12081                 };
12082             }
12083
12084         } else {
12085             if(this.removable && !this.editable ){
12086                 inputblock = {
12087                     cls : 'roo-removable',
12088                     cn :  [
12089                         inputblock,
12090                         {
12091                             tag: 'button',
12092                             html : 'x',
12093                             cls : 'roo-combo-removable-btn close'
12094                         }
12095                     ] 
12096                 };
12097             }
12098         }
12099         
12100         if (this.before || this.after) {
12101             
12102             inputblock = {
12103                 cls : 'input-group',
12104                 cn :  [] 
12105             };
12106             if (this.before) {
12107                 inputblock.cn.push({
12108                     tag :'span',
12109                     cls : 'input-group-addon input-group-prepend input-group-text',
12110                     html : this.before
12111                 });
12112             }
12113             
12114             inputblock.cn.push(input);
12115             
12116             if(this.hasFeedback && !this.allowBlank){
12117                 inputblock.cls += ' has-feedback';
12118                 inputblock.cn.push(feedback);
12119             }
12120             
12121             if (this.after) {
12122                 inputblock.cn.push({
12123                     tag :'span',
12124                     cls : 'input-group-addon input-group-append input-group-text',
12125                     html : this.after
12126                 });
12127             }
12128             
12129         };
12130         
12131       
12132         
12133         var ibwrap = inputblock;
12134         
12135         if(this.multiple){
12136             ibwrap = {
12137                 tag: 'ul',
12138                 cls: 'roo-select2-choices',
12139                 cn:[
12140                     {
12141                         tag: 'li',
12142                         cls: 'roo-select2-search-field',
12143                         cn: [
12144
12145                             inputblock
12146                         ]
12147                     }
12148                 ]
12149             };
12150                 
12151         }
12152         
12153         var combobox = {
12154             cls: 'roo-select2-container input-group',
12155             cn: [
12156                  {
12157                     tag: 'input',
12158                     type : 'hidden',
12159                     cls: 'form-hidden-field'
12160                 },
12161                 ibwrap
12162             ]
12163         };
12164         
12165         if(!this.multiple && this.showToggleBtn){
12166             
12167             var caret = {
12168                         tag: 'span',
12169                         cls: 'caret'
12170              };
12171             if (this.caret != false) {
12172                 caret = {
12173                      tag: 'i',
12174                      cls: 'fa fa-' + this.caret
12175                 };
12176                 
12177             }
12178             
12179             combobox.cn.push({
12180                 tag :'span',
12181                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12182                 cn : [
12183                     Roo.bootstrap.version == 3 ? caret : '',
12184                     {
12185                         tag: 'span',
12186                         cls: 'combobox-clear',
12187                         cn  : [
12188                             {
12189                                 tag : 'i',
12190                                 cls: 'icon-remove'
12191                             }
12192                         ]
12193                     }
12194                 ]
12195
12196             })
12197         }
12198         
12199         if(this.multiple){
12200             combobox.cls += ' roo-select2-container-multi';
12201         }
12202          var indicator = {
12203             tag : 'i',
12204             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12205             tooltip : 'This field is required'
12206         };
12207         if (Roo.bootstrap.version == 4) {
12208             indicator = {
12209                 tag : 'i',
12210                 style : 'display:none'
12211             };
12212         }
12213         
12214         
12215         if (align ==='left' && this.fieldLabel.length) {
12216             
12217             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12218
12219             cfg.cn = [
12220                 indicator,
12221                 {
12222                     tag: 'label',
12223                     'for' :  id,
12224                     cls : 'control-label',
12225                     html : this.fieldLabel
12226
12227                 },
12228                 {
12229                     cls : "", 
12230                     cn: [
12231                         combobox
12232                     ]
12233                 }
12234
12235             ];
12236             
12237             var labelCfg = cfg.cn[1];
12238             var contentCfg = cfg.cn[2];
12239             
12240             if(this.indicatorpos == 'right'){
12241                 cfg.cn = [
12242                     {
12243                         tag: 'label',
12244                         'for' :  id,
12245                         cls : 'control-label',
12246                         cn : [
12247                             {
12248                                 tag : 'span',
12249                                 html : this.fieldLabel
12250                             },
12251                             indicator
12252                         ]
12253                     },
12254                     {
12255                         cls : "", 
12256                         cn: [
12257                             combobox
12258                         ]
12259                     }
12260
12261                 ];
12262                 
12263                 labelCfg = cfg.cn[0];
12264                 contentCfg = cfg.cn[1];
12265             }
12266             
12267             if(this.labelWidth > 12){
12268                 labelCfg.style = "width: " + this.labelWidth + 'px';
12269             }
12270             
12271             if(this.labelWidth < 13 && this.labelmd == 0){
12272                 this.labelmd = this.labelWidth;
12273             }
12274             
12275             if(this.labellg > 0){
12276                 labelCfg.cls += ' col-lg-' + this.labellg;
12277                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12278             }
12279             
12280             if(this.labelmd > 0){
12281                 labelCfg.cls += ' col-md-' + this.labelmd;
12282                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12283             }
12284             
12285             if(this.labelsm > 0){
12286                 labelCfg.cls += ' col-sm-' + this.labelsm;
12287                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12288             }
12289             
12290             if(this.labelxs > 0){
12291                 labelCfg.cls += ' col-xs-' + this.labelxs;
12292                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12293             }
12294             
12295         } else if ( this.fieldLabel.length) {
12296 //                Roo.log(" label");
12297             cfg.cn = [
12298                 indicator,
12299                {
12300                    tag: 'label',
12301                    //cls : 'input-group-addon',
12302                    html : this.fieldLabel
12303
12304                },
12305
12306                combobox
12307
12308             ];
12309             
12310             if(this.indicatorpos == 'right'){
12311                 
12312                 cfg.cn = [
12313                     {
12314                        tag: 'label',
12315                        cn : [
12316                            {
12317                                tag : 'span',
12318                                html : this.fieldLabel
12319                            },
12320                            indicator
12321                        ]
12322
12323                     },
12324                     combobox
12325
12326                 ];
12327
12328             }
12329
12330         } else {
12331             
12332 //                Roo.log(" no label && no align");
12333                 cfg = combobox
12334                      
12335                 
12336         }
12337         
12338         var settings=this;
12339         ['xs','sm','md','lg'].map(function(size){
12340             if (settings[size]) {
12341                 cfg.cls += ' col-' + size + '-' + settings[size];
12342             }
12343         });
12344         
12345         return cfg;
12346         
12347     },
12348     
12349     
12350     
12351     // private
12352     onResize : function(w, h){
12353 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12354 //        if(typeof w == 'number'){
12355 //            var x = w - this.trigger.getWidth();
12356 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12357 //            this.trigger.setStyle('left', x+'px');
12358 //        }
12359     },
12360
12361     // private
12362     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12363
12364     // private
12365     getResizeEl : function(){
12366         return this.inputEl();
12367     },
12368
12369     // private
12370     getPositionEl : function(){
12371         return this.inputEl();
12372     },
12373
12374     // private
12375     alignErrorIcon : function(){
12376         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12377     },
12378
12379     // private
12380     initEvents : function(){
12381         
12382         this.createList();
12383         
12384         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12385         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12386         if(!this.multiple && this.showToggleBtn){
12387             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12388             if(this.hideTrigger){
12389                 this.trigger.setDisplayed(false);
12390             }
12391             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12392         }
12393         
12394         if(this.multiple){
12395             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12396         }
12397         
12398         if(this.removable && !this.editable && !this.tickable){
12399             var close = this.closeTriggerEl();
12400             
12401             if(close){
12402                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12403                 close.on('click', this.removeBtnClick, this, close);
12404             }
12405         }
12406         
12407         //this.trigger.addClassOnOver('x-form-trigger-over');
12408         //this.trigger.addClassOnClick('x-form-trigger-click');
12409         
12410         //if(!this.width){
12411         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12412         //}
12413     },
12414     
12415     closeTriggerEl : function()
12416     {
12417         var close = this.el.select('.roo-combo-removable-btn', true).first();
12418         return close ? close : false;
12419     },
12420     
12421     removeBtnClick : function(e, h, el)
12422     {
12423         e.preventDefault();
12424         
12425         if(this.fireEvent("remove", this) !== false){
12426             this.reset();
12427             this.fireEvent("afterremove", this)
12428         }
12429     },
12430     
12431     createList : function()
12432     {
12433         this.list = Roo.get(document.body).createChild({
12434             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12435             cls: 'typeahead typeahead-long dropdown-menu shadow',
12436             style: 'display:none'
12437         });
12438         
12439         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12440         
12441     },
12442
12443     // private
12444     initTrigger : function(){
12445        
12446     },
12447
12448     // private
12449     onDestroy : function(){
12450         if(this.trigger){
12451             this.trigger.removeAllListeners();
12452           //  this.trigger.remove();
12453         }
12454         //if(this.wrap){
12455         //    this.wrap.remove();
12456         //}
12457         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12458     },
12459
12460     // private
12461     onFocus : function(){
12462         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12463         /*
12464         if(!this.mimicing){
12465             this.wrap.addClass('x-trigger-wrap-focus');
12466             this.mimicing = true;
12467             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12468             if(this.monitorTab){
12469                 this.el.on("keydown", this.checkTab, this);
12470             }
12471         }
12472         */
12473     },
12474
12475     // private
12476     checkTab : function(e){
12477         if(e.getKey() == e.TAB){
12478             this.triggerBlur();
12479         }
12480     },
12481
12482     // private
12483     onBlur : function(){
12484         // do nothing
12485     },
12486
12487     // private
12488     mimicBlur : function(e, t){
12489         /*
12490         if(!this.wrap.contains(t) && this.validateBlur()){
12491             this.triggerBlur();
12492         }
12493         */
12494     },
12495
12496     // private
12497     triggerBlur : function(){
12498         this.mimicing = false;
12499         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12500         if(this.monitorTab){
12501             this.el.un("keydown", this.checkTab, this);
12502         }
12503         //this.wrap.removeClass('x-trigger-wrap-focus');
12504         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12505     },
12506
12507     // private
12508     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12509     validateBlur : function(e, t){
12510         return true;
12511     },
12512
12513     // private
12514     onDisable : function(){
12515         this.inputEl().dom.disabled = true;
12516         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12517         //if(this.wrap){
12518         //    this.wrap.addClass('x-item-disabled');
12519         //}
12520     },
12521
12522     // private
12523     onEnable : function(){
12524         this.inputEl().dom.disabled = false;
12525         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12526         //if(this.wrap){
12527         //    this.el.removeClass('x-item-disabled');
12528         //}
12529     },
12530
12531     // private
12532     onShow : function(){
12533         var ae = this.getActionEl();
12534         
12535         if(ae){
12536             ae.dom.style.display = '';
12537             ae.dom.style.visibility = 'visible';
12538         }
12539     },
12540
12541     // private
12542     
12543     onHide : function(){
12544         var ae = this.getActionEl();
12545         ae.dom.style.display = 'none';
12546     },
12547
12548     /**
12549      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12550      * by an implementing function.
12551      * @method
12552      * @param {EventObject} e
12553      */
12554     onTriggerClick : Roo.emptyFn
12555 });
12556  
12557 /*
12558 * Licence: LGPL
12559 */
12560
12561 /**
12562  * @class Roo.bootstrap.CardUploader
12563  * @extends Roo.bootstrap.Button
12564  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12565  * @cfg {Number} errorTimeout default 3000
12566  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12567  * @cfg {Array}  html The button text.
12568
12569  *
12570  * @constructor
12571  * Create a new CardUploader
12572  * @param {Object} config The config object
12573  */
12574
12575 Roo.bootstrap.CardUploader = function(config){
12576     
12577  
12578     
12579     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12580     
12581     
12582     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12583         return r.data.id
12584      });
12585     
12586      this.addEvents({
12587          // raw events
12588         /**
12589          * @event preview
12590          * When a image is clicked on - and needs to display a slideshow or similar..
12591          * @param {Roo.bootstrap.Card} this
12592          * @param {Object} The image information data 
12593          *
12594          */
12595         'preview' : true,
12596          /**
12597          * @event download
12598          * When a the download link is clicked
12599          * @param {Roo.bootstrap.Card} this
12600          * @param {Object} The image information data  contains 
12601          */
12602         'download' : true
12603         
12604     });
12605 };
12606  
12607 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12608     
12609      
12610     errorTimeout : 3000,
12611      
12612     images : false,
12613    
12614     fileCollection : false,
12615     allowBlank : true,
12616     
12617     getAutoCreate : function()
12618     {
12619         
12620         var cfg =  {
12621             cls :'form-group' ,
12622             cn : [
12623                
12624                 {
12625                     tag: 'label',
12626                    //cls : 'input-group-addon',
12627                     html : this.fieldLabel
12628
12629                 },
12630
12631                 {
12632                     tag: 'input',
12633                     type : 'hidden',
12634                     name : this.name,
12635                     value : this.value,
12636                     cls : 'd-none  form-control'
12637                 },
12638                 
12639                 {
12640                     tag: 'input',
12641                     multiple : 'multiple',
12642                     type : 'file',
12643                     cls : 'd-none  roo-card-upload-selector'
12644                 },
12645                 
12646                 {
12647                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12648                 },
12649                 {
12650                     cls : 'card-columns roo-card-uploader-container'
12651                 }
12652
12653             ]
12654         };
12655            
12656          
12657         return cfg;
12658     },
12659     
12660     getChildContainer : function() /// what children are added to.
12661     {
12662         return this.containerEl;
12663     },
12664    
12665     getButtonContainer : function() /// what children are added to.
12666     {
12667         return this.el.select(".roo-card-uploader-button-container").first();
12668     },
12669    
12670     initEvents : function()
12671     {
12672         
12673         Roo.bootstrap.Input.prototype.initEvents.call(this);
12674         
12675         var t = this;
12676         this.addxtype({
12677             xns: Roo.bootstrap,
12678
12679             xtype : 'Button',
12680             container_method : 'getButtonContainer' ,            
12681             html :  this.html, // fix changable?
12682             cls : 'w-100 ',
12683             listeners : {
12684                 'click' : function(btn, e) {
12685                     t.onClick(e);
12686                 }
12687             }
12688         });
12689         
12690         
12691         
12692         
12693         this.urlAPI = (window.createObjectURL && window) || 
12694                                 (window.URL && URL.revokeObjectURL && URL) || 
12695                                 (window.webkitURL && webkitURL);
12696                         
12697          
12698          
12699          
12700         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12701         
12702         this.selectorEl.on('change', this.onFileSelected, this);
12703         if (this.images) {
12704             var t = this;
12705             this.images.forEach(function(img) {
12706                 t.addCard(img)
12707             });
12708             this.images = false;
12709         }
12710         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12711          
12712        
12713     },
12714     
12715    
12716     onClick : function(e)
12717     {
12718         e.preventDefault();
12719          
12720         this.selectorEl.dom.click();
12721          
12722     },
12723     
12724     onFileSelected : function(e)
12725     {
12726         e.preventDefault();
12727         
12728         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12729             return;
12730         }
12731         
12732         Roo.each(this.selectorEl.dom.files, function(file){    
12733             this.addFile(file);
12734         }, this);
12735          
12736     },
12737     
12738       
12739     
12740       
12741     
12742     addFile : function(file)
12743     {
12744            
12745         if(typeof(file) === 'string'){
12746             throw "Add file by name?"; // should not happen
12747             return;
12748         }
12749         
12750         if(!file || !this.urlAPI){
12751             return;
12752         }
12753         
12754         // file;
12755         // file.type;
12756         
12757         var _this = this;
12758         
12759         
12760         var url = _this.urlAPI.createObjectURL( file);
12761            
12762         this.addCard({
12763             id : Roo.bootstrap.CardUploader.ID--,
12764             is_uploaded : false,
12765             src : url,
12766             srcfile : file,
12767             title : file.name,
12768             mimetype : file.type,
12769             preview : false,
12770             is_deleted : 0
12771         });
12772         
12773     },
12774     
12775     /**
12776      * addCard - add an Attachment to the uploader
12777      * @param data - the data about the image to upload
12778      *
12779      * {
12780           id : 123
12781           title : "Title of file",
12782           is_uploaded : false,
12783           src : "http://.....",
12784           srcfile : { the File upload object },
12785           mimetype : file.type,
12786           preview : false,
12787           is_deleted : 0
12788           .. any other data...
12789         }
12790      *
12791      * 
12792     */
12793     
12794     addCard : function (data)
12795     {
12796         // hidden input element?
12797         // if the file is not an image...
12798         //then we need to use something other that and header_image
12799         var t = this;
12800         //   remove.....
12801         var footer = [
12802             {
12803                 xns : Roo.bootstrap,
12804                 xtype : 'CardFooter',
12805                  items: [
12806                     {
12807                         xns : Roo.bootstrap,
12808                         xtype : 'Element',
12809                         cls : 'd-flex',
12810                         items : [
12811                             
12812                             {
12813                                 xns : Roo.bootstrap,
12814                                 xtype : 'Button',
12815                                 html : String.format("<small>{0}</small>", data.title),
12816                                 cls : 'col-10 text-left',
12817                                 size: 'sm',
12818                                 weight: 'link',
12819                                 fa : 'download',
12820                                 listeners : {
12821                                     click : function() {
12822                                      
12823                                         t.fireEvent( "download", t, data );
12824                                     }
12825                                 }
12826                             },
12827                           
12828                             {
12829                                 xns : Roo.bootstrap,
12830                                 xtype : 'Button',
12831                                 style: 'max-height: 28px; ',
12832                                 size : 'sm',
12833                                 weight: 'danger',
12834                                 cls : 'col-2',
12835                                 fa : 'times',
12836                                 listeners : {
12837                                     click : function() {
12838                                         t.removeCard(data.id)
12839                                     }
12840                                 }
12841                             }
12842                         ]
12843                     }
12844                     
12845                 ] 
12846             }
12847             
12848         ];
12849         
12850         var cn = this.addxtype(
12851             {
12852                  
12853                 xns : Roo.bootstrap,
12854                 xtype : 'Card',
12855                 closeable : true,
12856                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12857                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12858                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12859                 data : data,
12860                 html : false,
12861                  
12862                 items : footer,
12863                 initEvents : function() {
12864                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12865                     var card = this;
12866                     this.imgEl = this.el.select('.card-img-top').first();
12867                     if (this.imgEl) {
12868                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12869                         this.imgEl.set({ 'pointer' : 'cursor' });
12870                                   
12871                     }
12872                     this.getCardFooter().addClass('p-1');
12873                     
12874                   
12875                 }
12876                 
12877             }
12878         );
12879         // dont' really need ot update items.
12880         // this.items.push(cn);
12881         this.fileCollection.add(cn);
12882         
12883         if (!data.srcfile) {
12884             this.updateInput();
12885             return;
12886         }
12887             
12888         var _t = this;
12889         var reader = new FileReader();
12890         reader.addEventListener("load", function() {  
12891             data.srcdata =  reader.result;
12892             _t.updateInput();
12893         });
12894         reader.readAsDataURL(data.srcfile);
12895         
12896         
12897         
12898     },
12899     removeCard : function(id)
12900     {
12901         
12902         var card  = this.fileCollection.get(id);
12903         card.data.is_deleted = 1;
12904         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12905         //this.fileCollection.remove(card);
12906         //this.items = this.items.filter(function(e) { return e != card });
12907         // dont' really need ot update items.
12908         card.el.dom.parentNode.removeChild(card.el.dom);
12909         this.updateInput();
12910
12911         
12912     },
12913     reset: function()
12914     {
12915         this.fileCollection.each(function(card) {
12916             if (card.el.dom && card.el.dom.parentNode) {
12917                 card.el.dom.parentNode.removeChild(card.el.dom);
12918             }
12919         });
12920         this.fileCollection.clear();
12921         this.updateInput();
12922     },
12923     
12924     updateInput : function()
12925     {
12926          var data = [];
12927         this.fileCollection.each(function(e) {
12928             data.push(e.data);
12929             
12930         });
12931         this.inputEl().dom.value = JSON.stringify(data);
12932         
12933         
12934         
12935     }
12936     
12937     
12938 });
12939
12940
12941 Roo.bootstrap.CardUploader.ID = -1;/*
12942  * Based on:
12943  * Ext JS Library 1.1.1
12944  * Copyright(c) 2006-2007, Ext JS, LLC.
12945  *
12946  * Originally Released Under LGPL - original licence link has changed is not relivant.
12947  *
12948  * Fork - LGPL
12949  * <script type="text/javascript">
12950  */
12951
12952
12953 /**
12954  * @class Roo.data.SortTypes
12955  * @singleton
12956  * Defines the default sorting (casting?) comparison functions used when sorting data.
12957  */
12958 Roo.data.SortTypes = {
12959     /**
12960      * Default sort that does nothing
12961      * @param {Mixed} s The value being converted
12962      * @return {Mixed} The comparison value
12963      */
12964     none : function(s){
12965         return s;
12966     },
12967     
12968     /**
12969      * The regular expression used to strip tags
12970      * @type {RegExp}
12971      * @property
12972      */
12973     stripTagsRE : /<\/?[^>]+>/gi,
12974     
12975     /**
12976      * Strips all HTML tags to sort on text only
12977      * @param {Mixed} s The value being converted
12978      * @return {String} The comparison value
12979      */
12980     asText : function(s){
12981         return String(s).replace(this.stripTagsRE, "");
12982     },
12983     
12984     /**
12985      * Strips all HTML tags to sort on text only - Case insensitive
12986      * @param {Mixed} s The value being converted
12987      * @return {String} The comparison value
12988      */
12989     asUCText : function(s){
12990         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12991     },
12992     
12993     /**
12994      * Case insensitive string
12995      * @param {Mixed} s The value being converted
12996      * @return {String} The comparison value
12997      */
12998     asUCString : function(s) {
12999         return String(s).toUpperCase();
13000     },
13001     
13002     /**
13003      * Date sorting
13004      * @param {Mixed} s The value being converted
13005      * @return {Number} The comparison value
13006      */
13007     asDate : function(s) {
13008         if(!s){
13009             return 0;
13010         }
13011         if(s instanceof Date){
13012             return s.getTime();
13013         }
13014         return Date.parse(String(s));
13015     },
13016     
13017     /**
13018      * Float sorting
13019      * @param {Mixed} s The value being converted
13020      * @return {Float} The comparison value
13021      */
13022     asFloat : function(s) {
13023         var val = parseFloat(String(s).replace(/,/g, ""));
13024         if(isNaN(val)) {
13025             val = 0;
13026         }
13027         return val;
13028     },
13029     
13030     /**
13031      * Integer sorting
13032      * @param {Mixed} s The value being converted
13033      * @return {Number} The comparison value
13034      */
13035     asInt : function(s) {
13036         var val = parseInt(String(s).replace(/,/g, ""));
13037         if(isNaN(val)) {
13038             val = 0;
13039         }
13040         return val;
13041     }
13042 };/*
13043  * Based on:
13044  * Ext JS Library 1.1.1
13045  * Copyright(c) 2006-2007, Ext JS, LLC.
13046  *
13047  * Originally Released Under LGPL - original licence link has changed is not relivant.
13048  *
13049  * Fork - LGPL
13050  * <script type="text/javascript">
13051  */
13052
13053 /**
13054 * @class Roo.data.Record
13055  * Instances of this class encapsulate both record <em>definition</em> information, and record
13056  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13057  * to access Records cached in an {@link Roo.data.Store} object.<br>
13058  * <p>
13059  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13060  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13061  * objects.<br>
13062  * <p>
13063  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13064  * @constructor
13065  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13066  * {@link #create}. The parameters are the same.
13067  * @param {Array} data An associative Array of data values keyed by the field name.
13068  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13069  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13070  * not specified an integer id is generated.
13071  */
13072 Roo.data.Record = function(data, id){
13073     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13074     this.data = data;
13075 };
13076
13077 /**
13078  * Generate a constructor for a specific record layout.
13079  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13080  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13081  * Each field definition object may contain the following properties: <ul>
13082  * <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,
13083  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13084  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13085  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13086  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13087  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13088  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13089  * this may be omitted.</p></li>
13090  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13091  * <ul><li>auto (Default, implies no conversion)</li>
13092  * <li>string</li>
13093  * <li>int</li>
13094  * <li>float</li>
13095  * <li>boolean</li>
13096  * <li>date</li></ul></p></li>
13097  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13098  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13099  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13100  * by the Reader into an object that will be stored in the Record. It is passed the
13101  * following parameters:<ul>
13102  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13103  * </ul></p></li>
13104  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13105  * </ul>
13106  * <br>usage:<br><pre><code>
13107 var TopicRecord = Roo.data.Record.create(
13108     {name: 'title', mapping: 'topic_title'},
13109     {name: 'author', mapping: 'username'},
13110     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13111     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13112     {name: 'lastPoster', mapping: 'user2'},
13113     {name: 'excerpt', mapping: 'post_text'}
13114 );
13115
13116 var myNewRecord = new TopicRecord({
13117     title: 'Do my job please',
13118     author: 'noobie',
13119     totalPosts: 1,
13120     lastPost: new Date(),
13121     lastPoster: 'Animal',
13122     excerpt: 'No way dude!'
13123 });
13124 myStore.add(myNewRecord);
13125 </code></pre>
13126  * @method create
13127  * @static
13128  */
13129 Roo.data.Record.create = function(o){
13130     var f = function(){
13131         f.superclass.constructor.apply(this, arguments);
13132     };
13133     Roo.extend(f, Roo.data.Record);
13134     var p = f.prototype;
13135     p.fields = new Roo.util.MixedCollection(false, function(field){
13136         return field.name;
13137     });
13138     for(var i = 0, len = o.length; i < len; i++){
13139         p.fields.add(new Roo.data.Field(o[i]));
13140     }
13141     f.getField = function(name){
13142         return p.fields.get(name);  
13143     };
13144     return f;
13145 };
13146
13147 Roo.data.Record.AUTO_ID = 1000;
13148 Roo.data.Record.EDIT = 'edit';
13149 Roo.data.Record.REJECT = 'reject';
13150 Roo.data.Record.COMMIT = 'commit';
13151
13152 Roo.data.Record.prototype = {
13153     /**
13154      * Readonly flag - true if this record has been modified.
13155      * @type Boolean
13156      */
13157     dirty : false,
13158     editing : false,
13159     error: null,
13160     modified: null,
13161
13162     // private
13163     join : function(store){
13164         this.store = store;
13165     },
13166
13167     /**
13168      * Set the named field to the specified value.
13169      * @param {String} name The name of the field to set.
13170      * @param {Object} value The value to set the field to.
13171      */
13172     set : function(name, value){
13173         if(this.data[name] == value){
13174             return;
13175         }
13176         this.dirty = true;
13177         if(!this.modified){
13178             this.modified = {};
13179         }
13180         if(typeof this.modified[name] == 'undefined'){
13181             this.modified[name] = this.data[name];
13182         }
13183         this.data[name] = value;
13184         if(!this.editing && this.store){
13185             this.store.afterEdit(this);
13186         }       
13187     },
13188
13189     /**
13190      * Get the value of the named field.
13191      * @param {String} name The name of the field to get the value of.
13192      * @return {Object} The value of the field.
13193      */
13194     get : function(name){
13195         return this.data[name]; 
13196     },
13197
13198     // private
13199     beginEdit : function(){
13200         this.editing = true;
13201         this.modified = {}; 
13202     },
13203
13204     // private
13205     cancelEdit : function(){
13206         this.editing = false;
13207         delete this.modified;
13208     },
13209
13210     // private
13211     endEdit : function(){
13212         this.editing = false;
13213         if(this.dirty && this.store){
13214             this.store.afterEdit(this);
13215         }
13216     },
13217
13218     /**
13219      * Usually called by the {@link Roo.data.Store} which owns the Record.
13220      * Rejects all changes made to the Record since either creation, or the last commit operation.
13221      * Modified fields are reverted to their original values.
13222      * <p>
13223      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13224      * of reject operations.
13225      */
13226     reject : function(){
13227         var m = this.modified;
13228         for(var n in m){
13229             if(typeof m[n] != "function"){
13230                 this.data[n] = m[n];
13231             }
13232         }
13233         this.dirty = false;
13234         delete this.modified;
13235         this.editing = false;
13236         if(this.store){
13237             this.store.afterReject(this);
13238         }
13239     },
13240
13241     /**
13242      * Usually called by the {@link Roo.data.Store} which owns the Record.
13243      * Commits all changes made to the Record since either creation, or the last commit operation.
13244      * <p>
13245      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13246      * of commit operations.
13247      */
13248     commit : function(){
13249         this.dirty = false;
13250         delete this.modified;
13251         this.editing = false;
13252         if(this.store){
13253             this.store.afterCommit(this);
13254         }
13255     },
13256
13257     // private
13258     hasError : function(){
13259         return this.error != null;
13260     },
13261
13262     // private
13263     clearError : function(){
13264         this.error = null;
13265     },
13266
13267     /**
13268      * Creates a copy of this record.
13269      * @param {String} id (optional) A new record id if you don't want to use this record's id
13270      * @return {Record}
13271      */
13272     copy : function(newId) {
13273         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13274     }
13275 };/*
13276  * Based on:
13277  * Ext JS Library 1.1.1
13278  * Copyright(c) 2006-2007, Ext JS, LLC.
13279  *
13280  * Originally Released Under LGPL - original licence link has changed is not relivant.
13281  *
13282  * Fork - LGPL
13283  * <script type="text/javascript">
13284  */
13285
13286
13287
13288 /**
13289  * @class Roo.data.Store
13290  * @extends Roo.util.Observable
13291  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13292  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13293  * <p>
13294  * 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
13295  * has no knowledge of the format of the data returned by the Proxy.<br>
13296  * <p>
13297  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13298  * instances from the data object. These records are cached and made available through accessor functions.
13299  * @constructor
13300  * Creates a new Store.
13301  * @param {Object} config A config object containing the objects needed for the Store to access data,
13302  * and read the data into Records.
13303  */
13304 Roo.data.Store = function(config){
13305     this.data = new Roo.util.MixedCollection(false);
13306     this.data.getKey = function(o){
13307         return o.id;
13308     };
13309     this.baseParams = {};
13310     // private
13311     this.paramNames = {
13312         "start" : "start",
13313         "limit" : "limit",
13314         "sort" : "sort",
13315         "dir" : "dir",
13316         "multisort" : "_multisort"
13317     };
13318
13319     if(config && config.data){
13320         this.inlineData = config.data;
13321         delete config.data;
13322     }
13323
13324     Roo.apply(this, config);
13325     
13326     if(this.reader){ // reader passed
13327         this.reader = Roo.factory(this.reader, Roo.data);
13328         this.reader.xmodule = this.xmodule || false;
13329         if(!this.recordType){
13330             this.recordType = this.reader.recordType;
13331         }
13332         if(this.reader.onMetaChange){
13333             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13334         }
13335     }
13336
13337     if(this.recordType){
13338         this.fields = this.recordType.prototype.fields;
13339     }
13340     this.modified = [];
13341
13342     this.addEvents({
13343         /**
13344          * @event datachanged
13345          * Fires when the data cache has changed, and a widget which is using this Store
13346          * as a Record cache should refresh its view.
13347          * @param {Store} this
13348          */
13349         datachanged : true,
13350         /**
13351          * @event metachange
13352          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13353          * @param {Store} this
13354          * @param {Object} meta The JSON metadata
13355          */
13356         metachange : true,
13357         /**
13358          * @event add
13359          * Fires when Records have been added to the Store
13360          * @param {Store} this
13361          * @param {Roo.data.Record[]} records The array of Records added
13362          * @param {Number} index The index at which the record(s) were added
13363          */
13364         add : true,
13365         /**
13366          * @event remove
13367          * Fires when a Record has been removed from the Store
13368          * @param {Store} this
13369          * @param {Roo.data.Record} record The Record that was removed
13370          * @param {Number} index The index at which the record was removed
13371          */
13372         remove : true,
13373         /**
13374          * @event update
13375          * Fires when a Record has been updated
13376          * @param {Store} this
13377          * @param {Roo.data.Record} record The Record that was updated
13378          * @param {String} operation The update operation being performed.  Value may be one of:
13379          * <pre><code>
13380  Roo.data.Record.EDIT
13381  Roo.data.Record.REJECT
13382  Roo.data.Record.COMMIT
13383          * </code></pre>
13384          */
13385         update : true,
13386         /**
13387          * @event clear
13388          * Fires when the data cache has been cleared.
13389          * @param {Store} this
13390          */
13391         clear : true,
13392         /**
13393          * @event beforeload
13394          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13395          * the load action will be canceled.
13396          * @param {Store} this
13397          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13398          */
13399         beforeload : true,
13400         /**
13401          * @event beforeloadadd
13402          * Fires after a new set of Records has been loaded.
13403          * @param {Store} this
13404          * @param {Roo.data.Record[]} records The Records that were loaded
13405          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13406          */
13407         beforeloadadd : true,
13408         /**
13409          * @event load
13410          * Fires after a new set of Records has been loaded, before they are added to the store.
13411          * @param {Store} this
13412          * @param {Roo.data.Record[]} records The Records that were loaded
13413          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13414          * @params {Object} return from reader
13415          */
13416         load : true,
13417         /**
13418          * @event loadexception
13419          * Fires if an exception occurs in the Proxy during loading.
13420          * Called with the signature of the Proxy's "loadexception" event.
13421          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13422          * 
13423          * @param {Proxy} 
13424          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13425          * @param {Object} load options 
13426          * @param {Object} jsonData from your request (normally this contains the Exception)
13427          */
13428         loadexception : true
13429     });
13430     
13431     if(this.proxy){
13432         this.proxy = Roo.factory(this.proxy, Roo.data);
13433         this.proxy.xmodule = this.xmodule || false;
13434         this.relayEvents(this.proxy,  ["loadexception"]);
13435     }
13436     this.sortToggle = {};
13437     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13438
13439     Roo.data.Store.superclass.constructor.call(this);
13440
13441     if(this.inlineData){
13442         this.loadData(this.inlineData);
13443         delete this.inlineData;
13444     }
13445 };
13446
13447 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13448      /**
13449     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13450     * without a remote query - used by combo/forms at present.
13451     */
13452     
13453     /**
13454     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13455     */
13456     /**
13457     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13458     */
13459     /**
13460     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13461     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13462     */
13463     /**
13464     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13465     * on any HTTP request
13466     */
13467     /**
13468     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13469     */
13470     /**
13471     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13472     */
13473     multiSort: false,
13474     /**
13475     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13476     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13477     */
13478     remoteSort : false,
13479
13480     /**
13481     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13482      * loaded or when a record is removed. (defaults to false).
13483     */
13484     pruneModifiedRecords : false,
13485
13486     // private
13487     lastOptions : null,
13488
13489     /**
13490      * Add Records to the Store and fires the add event.
13491      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13492      */
13493     add : function(records){
13494         records = [].concat(records);
13495         for(var i = 0, len = records.length; i < len; i++){
13496             records[i].join(this);
13497         }
13498         var index = this.data.length;
13499         this.data.addAll(records);
13500         this.fireEvent("add", this, records, index);
13501     },
13502
13503     /**
13504      * Remove a Record from the Store and fires the remove event.
13505      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13506      */
13507     remove : function(record){
13508         var index = this.data.indexOf(record);
13509         this.data.removeAt(index);
13510  
13511         if(this.pruneModifiedRecords){
13512             this.modified.remove(record);
13513         }
13514         this.fireEvent("remove", this, record, index);
13515     },
13516
13517     /**
13518      * Remove all Records from the Store and fires the clear event.
13519      */
13520     removeAll : function(){
13521         this.data.clear();
13522         if(this.pruneModifiedRecords){
13523             this.modified = [];
13524         }
13525         this.fireEvent("clear", this);
13526     },
13527
13528     /**
13529      * Inserts Records to the Store at the given index and fires the add event.
13530      * @param {Number} index The start index at which to insert the passed Records.
13531      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13532      */
13533     insert : function(index, records){
13534         records = [].concat(records);
13535         for(var i = 0, len = records.length; i < len; i++){
13536             this.data.insert(index, records[i]);
13537             records[i].join(this);
13538         }
13539         this.fireEvent("add", this, records, index);
13540     },
13541
13542     /**
13543      * Get the index within the cache of the passed Record.
13544      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13545      * @return {Number} The index of the passed Record. Returns -1 if not found.
13546      */
13547     indexOf : function(record){
13548         return this.data.indexOf(record);
13549     },
13550
13551     /**
13552      * Get the index within the cache of the Record with the passed id.
13553      * @param {String} id The id of the Record to find.
13554      * @return {Number} The index of the Record. Returns -1 if not found.
13555      */
13556     indexOfId : function(id){
13557         return this.data.indexOfKey(id);
13558     },
13559
13560     /**
13561      * Get the Record with the specified id.
13562      * @param {String} id The id of the Record to find.
13563      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13564      */
13565     getById : function(id){
13566         return this.data.key(id);
13567     },
13568
13569     /**
13570      * Get the Record at the specified index.
13571      * @param {Number} index The index of the Record to find.
13572      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13573      */
13574     getAt : function(index){
13575         return this.data.itemAt(index);
13576     },
13577
13578     /**
13579      * Returns a range of Records between specified indices.
13580      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13581      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13582      * @return {Roo.data.Record[]} An array of Records
13583      */
13584     getRange : function(start, end){
13585         return this.data.getRange(start, end);
13586     },
13587
13588     // private
13589     storeOptions : function(o){
13590         o = Roo.apply({}, o);
13591         delete o.callback;
13592         delete o.scope;
13593         this.lastOptions = o;
13594     },
13595
13596     /**
13597      * Loads the Record cache from the configured Proxy using the configured Reader.
13598      * <p>
13599      * If using remote paging, then the first load call must specify the <em>start</em>
13600      * and <em>limit</em> properties in the options.params property to establish the initial
13601      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13602      * <p>
13603      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13604      * and this call will return before the new data has been loaded. Perform any post-processing
13605      * in a callback function, or in a "load" event handler.</strong>
13606      * <p>
13607      * @param {Object} options An object containing properties which control loading options:<ul>
13608      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13609      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13610      * passed the following arguments:<ul>
13611      * <li>r : Roo.data.Record[]</li>
13612      * <li>options: Options object from the load call</li>
13613      * <li>success: Boolean success indicator</li></ul></li>
13614      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13615      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13616      * </ul>
13617      */
13618     load : function(options){
13619         options = options || {};
13620         if(this.fireEvent("beforeload", this, options) !== false){
13621             this.storeOptions(options);
13622             var p = Roo.apply(options.params || {}, this.baseParams);
13623             // if meta was not loaded from remote source.. try requesting it.
13624             if (!this.reader.metaFromRemote) {
13625                 p._requestMeta = 1;
13626             }
13627             if(this.sortInfo && this.remoteSort){
13628                 var pn = this.paramNames;
13629                 p[pn["sort"]] = this.sortInfo.field;
13630                 p[pn["dir"]] = this.sortInfo.direction;
13631             }
13632             if (this.multiSort) {
13633                 var pn = this.paramNames;
13634                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13635             }
13636             
13637             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13638         }
13639     },
13640
13641     /**
13642      * Reloads the Record cache from the configured Proxy using the configured Reader and
13643      * the options from the last load operation performed.
13644      * @param {Object} options (optional) An object containing properties which may override the options
13645      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13646      * the most recently used options are reused).
13647      */
13648     reload : function(options){
13649         this.load(Roo.applyIf(options||{}, this.lastOptions));
13650     },
13651
13652     // private
13653     // Called as a callback by the Reader during a load operation.
13654     loadRecords : function(o, options, success){
13655         if(!o || success === false){
13656             if(success !== false){
13657                 this.fireEvent("load", this, [], options, o);
13658             }
13659             if(options.callback){
13660                 options.callback.call(options.scope || this, [], options, false);
13661             }
13662             return;
13663         }
13664         // if data returned failure - throw an exception.
13665         if (o.success === false) {
13666             // show a message if no listener is registered.
13667             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13668                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13669             }
13670             // loadmask wil be hooked into this..
13671             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13672             return;
13673         }
13674         var r = o.records, t = o.totalRecords || r.length;
13675         
13676         this.fireEvent("beforeloadadd", this, r, options, o);
13677         
13678         if(!options || options.add !== true){
13679             if(this.pruneModifiedRecords){
13680                 this.modified = [];
13681             }
13682             for(var i = 0, len = r.length; i < len; i++){
13683                 r[i].join(this);
13684             }
13685             if(this.snapshot){
13686                 this.data = this.snapshot;
13687                 delete this.snapshot;
13688             }
13689             this.data.clear();
13690             this.data.addAll(r);
13691             this.totalLength = t;
13692             this.applySort();
13693             this.fireEvent("datachanged", this);
13694         }else{
13695             this.totalLength = Math.max(t, this.data.length+r.length);
13696             this.add(r);
13697         }
13698         
13699         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13700                 
13701             var e = new Roo.data.Record({});
13702
13703             e.set(this.parent.displayField, this.parent.emptyTitle);
13704             e.set(this.parent.valueField, '');
13705
13706             this.insert(0, e);
13707         }
13708             
13709         this.fireEvent("load", this, r, options, o);
13710         if(options.callback){
13711             options.callback.call(options.scope || this, r, options, true);
13712         }
13713     },
13714
13715
13716     /**
13717      * Loads data from a passed data block. A Reader which understands the format of the data
13718      * must have been configured in the constructor.
13719      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13720      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13721      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13722      */
13723     loadData : function(o, append){
13724         var r = this.reader.readRecords(o);
13725         this.loadRecords(r, {add: append}, true);
13726     },
13727     
13728      /**
13729      * using 'cn' the nested child reader read the child array into it's child stores.
13730      * @param {Object} rec The record with a 'children array
13731      */
13732     loadDataFromChildren : function(rec)
13733     {
13734         this.loadData(this.reader.toLoadData(rec));
13735     },
13736     
13737
13738     /**
13739      * Gets the number of cached records.
13740      * <p>
13741      * <em>If using paging, this may not be the total size of the dataset. If the data object
13742      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13743      * the data set size</em>
13744      */
13745     getCount : function(){
13746         return this.data.length || 0;
13747     },
13748
13749     /**
13750      * Gets the total number of records in the dataset as returned by the server.
13751      * <p>
13752      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13753      * the dataset size</em>
13754      */
13755     getTotalCount : function(){
13756         return this.totalLength || 0;
13757     },
13758
13759     /**
13760      * Returns the sort state of the Store as an object with two properties:
13761      * <pre><code>
13762  field {String} The name of the field by which the Records are sorted
13763  direction {String} The sort order, "ASC" or "DESC"
13764      * </code></pre>
13765      */
13766     getSortState : function(){
13767         return this.sortInfo;
13768     },
13769
13770     // private
13771     applySort : function(){
13772         if(this.sortInfo && !this.remoteSort){
13773             var s = this.sortInfo, f = s.field;
13774             var st = this.fields.get(f).sortType;
13775             var fn = function(r1, r2){
13776                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13777                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13778             };
13779             this.data.sort(s.direction, fn);
13780             if(this.snapshot && this.snapshot != this.data){
13781                 this.snapshot.sort(s.direction, fn);
13782             }
13783         }
13784     },
13785
13786     /**
13787      * Sets the default sort column and order to be used by the next load operation.
13788      * @param {String} fieldName The name of the field to sort by.
13789      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13790      */
13791     setDefaultSort : function(field, dir){
13792         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13793     },
13794
13795     /**
13796      * Sort the Records.
13797      * If remote sorting is used, the sort is performed on the server, and the cache is
13798      * reloaded. If local sorting is used, the cache is sorted internally.
13799      * @param {String} fieldName The name of the field to sort by.
13800      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13801      */
13802     sort : function(fieldName, dir){
13803         var f = this.fields.get(fieldName);
13804         if(!dir){
13805             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13806             
13807             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13808                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13809             }else{
13810                 dir = f.sortDir;
13811             }
13812         }
13813         this.sortToggle[f.name] = dir;
13814         this.sortInfo = {field: f.name, direction: dir};
13815         if(!this.remoteSort){
13816             this.applySort();
13817             this.fireEvent("datachanged", this);
13818         }else{
13819             this.load(this.lastOptions);
13820         }
13821     },
13822
13823     /**
13824      * Calls the specified function for each of the Records in the cache.
13825      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13826      * Returning <em>false</em> aborts and exits the iteration.
13827      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13828      */
13829     each : function(fn, scope){
13830         this.data.each(fn, scope);
13831     },
13832
13833     /**
13834      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13835      * (e.g., during paging).
13836      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13837      */
13838     getModifiedRecords : function(){
13839         return this.modified;
13840     },
13841
13842     // private
13843     createFilterFn : function(property, value, anyMatch){
13844         if(!value.exec){ // not a regex
13845             value = String(value);
13846             if(value.length == 0){
13847                 return false;
13848             }
13849             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13850         }
13851         return function(r){
13852             return value.test(r.data[property]);
13853         };
13854     },
13855
13856     /**
13857      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13858      * @param {String} property A field on your records
13859      * @param {Number} start The record index to start at (defaults to 0)
13860      * @param {Number} end The last record index to include (defaults to length - 1)
13861      * @return {Number} The sum
13862      */
13863     sum : function(property, start, end){
13864         var rs = this.data.items, v = 0;
13865         start = start || 0;
13866         end = (end || end === 0) ? end : rs.length-1;
13867
13868         for(var i = start; i <= end; i++){
13869             v += (rs[i].data[property] || 0);
13870         }
13871         return v;
13872     },
13873
13874     /**
13875      * Filter the records by a specified property.
13876      * @param {String} field A field on your records
13877      * @param {String/RegExp} value Either a string that the field
13878      * should start with or a RegExp to test against the field
13879      * @param {Boolean} anyMatch True to match any part not just the beginning
13880      */
13881     filter : function(property, value, anyMatch){
13882         var fn = this.createFilterFn(property, value, anyMatch);
13883         return fn ? this.filterBy(fn) : this.clearFilter();
13884     },
13885
13886     /**
13887      * Filter by a function. The specified function will be called with each
13888      * record in this data source. If the function returns true the record is included,
13889      * otherwise it is filtered.
13890      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13891      * @param {Object} scope (optional) The scope of the function (defaults to this)
13892      */
13893     filterBy : function(fn, scope){
13894         this.snapshot = this.snapshot || this.data;
13895         this.data = this.queryBy(fn, scope||this);
13896         this.fireEvent("datachanged", this);
13897     },
13898
13899     /**
13900      * Query the records by a specified property.
13901      * @param {String} field A field on your records
13902      * @param {String/RegExp} value Either a string that the field
13903      * should start with or a RegExp to test against the field
13904      * @param {Boolean} anyMatch True to match any part not just the beginning
13905      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13906      */
13907     query : function(property, value, anyMatch){
13908         var fn = this.createFilterFn(property, value, anyMatch);
13909         return fn ? this.queryBy(fn) : this.data.clone();
13910     },
13911
13912     /**
13913      * Query by a function. The specified function will be called with each
13914      * record in this data source. If the function returns true the record is included
13915      * in the results.
13916      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13917      * @param {Object} scope (optional) The scope of the function (defaults to this)
13918       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13919      **/
13920     queryBy : function(fn, scope){
13921         var data = this.snapshot || this.data;
13922         return data.filterBy(fn, scope||this);
13923     },
13924
13925     /**
13926      * Collects unique values for a particular dataIndex from this store.
13927      * @param {String} dataIndex The property to collect
13928      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13929      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13930      * @return {Array} An array of the unique values
13931      **/
13932     collect : function(dataIndex, allowNull, bypassFilter){
13933         var d = (bypassFilter === true && this.snapshot) ?
13934                 this.snapshot.items : this.data.items;
13935         var v, sv, r = [], l = {};
13936         for(var i = 0, len = d.length; i < len; i++){
13937             v = d[i].data[dataIndex];
13938             sv = String(v);
13939             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13940                 l[sv] = true;
13941                 r[r.length] = v;
13942             }
13943         }
13944         return r;
13945     },
13946
13947     /**
13948      * Revert to a view of the Record cache with no filtering applied.
13949      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13950      */
13951     clearFilter : function(suppressEvent){
13952         if(this.snapshot && this.snapshot != this.data){
13953             this.data = this.snapshot;
13954             delete this.snapshot;
13955             if(suppressEvent !== true){
13956                 this.fireEvent("datachanged", this);
13957             }
13958         }
13959     },
13960
13961     // private
13962     afterEdit : function(record){
13963         if(this.modified.indexOf(record) == -1){
13964             this.modified.push(record);
13965         }
13966         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13967     },
13968     
13969     // private
13970     afterReject : function(record){
13971         this.modified.remove(record);
13972         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13973     },
13974
13975     // private
13976     afterCommit : function(record){
13977         this.modified.remove(record);
13978         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13979     },
13980
13981     /**
13982      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13983      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13984      */
13985     commitChanges : function(){
13986         var m = this.modified.slice(0);
13987         this.modified = [];
13988         for(var i = 0, len = m.length; i < len; i++){
13989             m[i].commit();
13990         }
13991     },
13992
13993     /**
13994      * Cancel outstanding changes on all changed records.
13995      */
13996     rejectChanges : function(){
13997         var m = this.modified.slice(0);
13998         this.modified = [];
13999         for(var i = 0, len = m.length; i < len; i++){
14000             m[i].reject();
14001         }
14002     },
14003
14004     onMetaChange : function(meta, rtype, o){
14005         this.recordType = rtype;
14006         this.fields = rtype.prototype.fields;
14007         delete this.snapshot;
14008         this.sortInfo = meta.sortInfo || this.sortInfo;
14009         this.modified = [];
14010         this.fireEvent('metachange', this, this.reader.meta);
14011     },
14012     
14013     moveIndex : function(data, type)
14014     {
14015         var index = this.indexOf(data);
14016         
14017         var newIndex = index + type;
14018         
14019         this.remove(data);
14020         
14021         this.insert(newIndex, data);
14022         
14023     }
14024 });/*
14025  * Based on:
14026  * Ext JS Library 1.1.1
14027  * Copyright(c) 2006-2007, Ext JS, LLC.
14028  *
14029  * Originally Released Under LGPL - original licence link has changed is not relivant.
14030  *
14031  * Fork - LGPL
14032  * <script type="text/javascript">
14033  */
14034
14035 /**
14036  * @class Roo.data.SimpleStore
14037  * @extends Roo.data.Store
14038  * Small helper class to make creating Stores from Array data easier.
14039  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14040  * @cfg {Array} fields An array of field definition objects, or field name strings.
14041  * @cfg {Object} an existing reader (eg. copied from another store)
14042  * @cfg {Array} data The multi-dimensional array of data
14043  * @constructor
14044  * @param {Object} config
14045  */
14046 Roo.data.SimpleStore = function(config)
14047 {
14048     Roo.data.SimpleStore.superclass.constructor.call(this, {
14049         isLocal : true,
14050         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14051                 id: config.id
14052             },
14053             Roo.data.Record.create(config.fields)
14054         ),
14055         proxy : new Roo.data.MemoryProxy(config.data)
14056     });
14057     this.load();
14058 };
14059 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14060  * Based on:
14061  * Ext JS Library 1.1.1
14062  * Copyright(c) 2006-2007, Ext JS, LLC.
14063  *
14064  * Originally Released Under LGPL - original licence link has changed is not relivant.
14065  *
14066  * Fork - LGPL
14067  * <script type="text/javascript">
14068  */
14069
14070 /**
14071 /**
14072  * @extends Roo.data.Store
14073  * @class Roo.data.JsonStore
14074  * Small helper class to make creating Stores for JSON data easier. <br/>
14075 <pre><code>
14076 var store = new Roo.data.JsonStore({
14077     url: 'get-images.php',
14078     root: 'images',
14079     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14080 });
14081 </code></pre>
14082  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14083  * JsonReader and HttpProxy (unless inline data is provided).</b>
14084  * @cfg {Array} fields An array of field definition objects, or field name strings.
14085  * @constructor
14086  * @param {Object} config
14087  */
14088 Roo.data.JsonStore = function(c){
14089     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14090         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14091         reader: new Roo.data.JsonReader(c, c.fields)
14092     }));
14093 };
14094 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14095  * Based on:
14096  * Ext JS Library 1.1.1
14097  * Copyright(c) 2006-2007, Ext JS, LLC.
14098  *
14099  * Originally Released Under LGPL - original licence link has changed is not relivant.
14100  *
14101  * Fork - LGPL
14102  * <script type="text/javascript">
14103  */
14104
14105  
14106 Roo.data.Field = function(config){
14107     if(typeof config == "string"){
14108         config = {name: config};
14109     }
14110     Roo.apply(this, config);
14111     
14112     if(!this.type){
14113         this.type = "auto";
14114     }
14115     
14116     var st = Roo.data.SortTypes;
14117     // named sortTypes are supported, here we look them up
14118     if(typeof this.sortType == "string"){
14119         this.sortType = st[this.sortType];
14120     }
14121     
14122     // set default sortType for strings and dates
14123     if(!this.sortType){
14124         switch(this.type){
14125             case "string":
14126                 this.sortType = st.asUCString;
14127                 break;
14128             case "date":
14129                 this.sortType = st.asDate;
14130                 break;
14131             default:
14132                 this.sortType = st.none;
14133         }
14134     }
14135
14136     // define once
14137     var stripRe = /[\$,%]/g;
14138
14139     // prebuilt conversion function for this field, instead of
14140     // switching every time we're reading a value
14141     if(!this.convert){
14142         var cv, dateFormat = this.dateFormat;
14143         switch(this.type){
14144             case "":
14145             case "auto":
14146             case undefined:
14147                 cv = function(v){ return v; };
14148                 break;
14149             case "string":
14150                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14151                 break;
14152             case "int":
14153                 cv = function(v){
14154                     return v !== undefined && v !== null && v !== '' ?
14155                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14156                     };
14157                 break;
14158             case "float":
14159                 cv = function(v){
14160                     return v !== undefined && v !== null && v !== '' ?
14161                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14162                     };
14163                 break;
14164             case "bool":
14165             case "boolean":
14166                 cv = function(v){ return v === true || v === "true" || v == 1; };
14167                 break;
14168             case "date":
14169                 cv = function(v){
14170                     if(!v){
14171                         return '';
14172                     }
14173                     if(v instanceof Date){
14174                         return v;
14175                     }
14176                     if(dateFormat){
14177                         if(dateFormat == "timestamp"){
14178                             return new Date(v*1000);
14179                         }
14180                         return Date.parseDate(v, dateFormat);
14181                     }
14182                     var parsed = Date.parse(v);
14183                     return parsed ? new Date(parsed) : null;
14184                 };
14185              break;
14186             
14187         }
14188         this.convert = cv;
14189     }
14190 };
14191
14192 Roo.data.Field.prototype = {
14193     dateFormat: null,
14194     defaultValue: "",
14195     mapping: null,
14196     sortType : null,
14197     sortDir : "ASC"
14198 };/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208  
14209 // Base class for reading structured data from a data source.  This class is intended to be
14210 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14211
14212 /**
14213  * @class Roo.data.DataReader
14214  * Base class for reading structured data from a data source.  This class is intended to be
14215  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14216  */
14217
14218 Roo.data.DataReader = function(meta, recordType){
14219     
14220     this.meta = meta;
14221     
14222     this.recordType = recordType instanceof Array ? 
14223         Roo.data.Record.create(recordType) : recordType;
14224 };
14225
14226 Roo.data.DataReader.prototype = {
14227     
14228     
14229     readerType : 'Data',
14230      /**
14231      * Create an empty record
14232      * @param {Object} data (optional) - overlay some values
14233      * @return {Roo.data.Record} record created.
14234      */
14235     newRow :  function(d) {
14236         var da =  {};
14237         this.recordType.prototype.fields.each(function(c) {
14238             switch( c.type) {
14239                 case 'int' : da[c.name] = 0; break;
14240                 case 'date' : da[c.name] = new Date(); break;
14241                 case 'float' : da[c.name] = 0.0; break;
14242                 case 'boolean' : da[c.name] = false; break;
14243                 default : da[c.name] = ""; break;
14244             }
14245             
14246         });
14247         return new this.recordType(Roo.apply(da, d));
14248     }
14249     
14250     
14251 };/*
14252  * Based on:
14253  * Ext JS Library 1.1.1
14254  * Copyright(c) 2006-2007, Ext JS, LLC.
14255  *
14256  * Originally Released Under LGPL - original licence link has changed is not relivant.
14257  *
14258  * Fork - LGPL
14259  * <script type="text/javascript">
14260  */
14261
14262 /**
14263  * @class Roo.data.DataProxy
14264  * @extends Roo.data.Observable
14265  * This class is an abstract base class for implementations which provide retrieval of
14266  * unformatted data objects.<br>
14267  * <p>
14268  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14269  * (of the appropriate type which knows how to parse the data object) to provide a block of
14270  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14271  * <p>
14272  * Custom implementations must implement the load method as described in
14273  * {@link Roo.data.HttpProxy#load}.
14274  */
14275 Roo.data.DataProxy = function(){
14276     this.addEvents({
14277         /**
14278          * @event beforeload
14279          * Fires before a network request is made to retrieve a data object.
14280          * @param {Object} This DataProxy object.
14281          * @param {Object} params The params parameter to the load function.
14282          */
14283         beforeload : true,
14284         /**
14285          * @event load
14286          * Fires before the load method's callback is called.
14287          * @param {Object} This DataProxy object.
14288          * @param {Object} o The data object.
14289          * @param {Object} arg The callback argument object passed to the load function.
14290          */
14291         load : true,
14292         /**
14293          * @event loadexception
14294          * Fires if an Exception occurs during data retrieval.
14295          * @param {Object} This DataProxy object.
14296          * @param {Object} o The data object.
14297          * @param {Object} arg The callback argument object passed to the load function.
14298          * @param {Object} e The Exception.
14299          */
14300         loadexception : true
14301     });
14302     Roo.data.DataProxy.superclass.constructor.call(this);
14303 };
14304
14305 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14306
14307     /**
14308      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14309      */
14310 /*
14311  * Based on:
14312  * Ext JS Library 1.1.1
14313  * Copyright(c) 2006-2007, Ext JS, LLC.
14314  *
14315  * Originally Released Under LGPL - original licence link has changed is not relivant.
14316  *
14317  * Fork - LGPL
14318  * <script type="text/javascript">
14319  */
14320 /**
14321  * @class Roo.data.MemoryProxy
14322  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14323  * to the Reader when its load method is called.
14324  * @constructor
14325  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14326  */
14327 Roo.data.MemoryProxy = function(data){
14328     if (data.data) {
14329         data = data.data;
14330     }
14331     Roo.data.MemoryProxy.superclass.constructor.call(this);
14332     this.data = data;
14333 };
14334
14335 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14336     
14337     /**
14338      * Load data from the requested source (in this case an in-memory
14339      * data object passed to the constructor), read the data object into
14340      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14341      * process that block using the passed callback.
14342      * @param {Object} params This parameter is not used by the MemoryProxy class.
14343      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14344      * object into a block of Roo.data.Records.
14345      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14346      * The function must be passed <ul>
14347      * <li>The Record block object</li>
14348      * <li>The "arg" argument from the load function</li>
14349      * <li>A boolean success indicator</li>
14350      * </ul>
14351      * @param {Object} scope The scope in which to call the callback
14352      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14353      */
14354     load : function(params, reader, callback, scope, arg){
14355         params = params || {};
14356         var result;
14357         try {
14358             result = reader.readRecords(params.data ? params.data :this.data);
14359         }catch(e){
14360             this.fireEvent("loadexception", this, arg, null, e);
14361             callback.call(scope, null, arg, false);
14362             return;
14363         }
14364         callback.call(scope, result, arg, true);
14365     },
14366     
14367     // private
14368     update : function(params, records){
14369         
14370     }
14371 });/*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381 /**
14382  * @class Roo.data.HttpProxy
14383  * @extends Roo.data.DataProxy
14384  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14385  * configured to reference a certain URL.<br><br>
14386  * <p>
14387  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14388  * from which the running page was served.<br><br>
14389  * <p>
14390  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14391  * <p>
14392  * Be aware that to enable the browser to parse an XML document, the server must set
14393  * the Content-Type header in the HTTP response to "text/xml".
14394  * @constructor
14395  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14396  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14397  * will be used to make the request.
14398  */
14399 Roo.data.HttpProxy = function(conn){
14400     Roo.data.HttpProxy.superclass.constructor.call(this);
14401     // is conn a conn config or a real conn?
14402     this.conn = conn;
14403     this.useAjax = !conn || !conn.events;
14404   
14405 };
14406
14407 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14408     // thse are take from connection...
14409     
14410     /**
14411      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14412      */
14413     /**
14414      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14415      * extra parameters to each request made by this object. (defaults to undefined)
14416      */
14417     /**
14418      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14419      *  to each request made by this object. (defaults to undefined)
14420      */
14421     /**
14422      * @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)
14423      */
14424     /**
14425      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14426      */
14427      /**
14428      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14429      * @type Boolean
14430      */
14431   
14432
14433     /**
14434      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14435      * @type Boolean
14436      */
14437     /**
14438      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14439      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14440      * a finer-grained basis than the DataProxy events.
14441      */
14442     getConnection : function(){
14443         return this.useAjax ? Roo.Ajax : this.conn;
14444     },
14445
14446     /**
14447      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14448      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14449      * process that block using the passed callback.
14450      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14451      * for the request to the remote server.
14452      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14453      * object into a block of Roo.data.Records.
14454      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14455      * The function must be passed <ul>
14456      * <li>The Record block object</li>
14457      * <li>The "arg" argument from the load function</li>
14458      * <li>A boolean success indicator</li>
14459      * </ul>
14460      * @param {Object} scope The scope in which to call the callback
14461      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14462      */
14463     load : function(params, reader, callback, scope, arg){
14464         if(this.fireEvent("beforeload", this, params) !== false){
14465             var  o = {
14466                 params : params || {},
14467                 request: {
14468                     callback : callback,
14469                     scope : scope,
14470                     arg : arg
14471                 },
14472                 reader: reader,
14473                 callback : this.loadResponse,
14474                 scope: this
14475             };
14476             if(this.useAjax){
14477                 Roo.applyIf(o, this.conn);
14478                 if(this.activeRequest){
14479                     Roo.Ajax.abort(this.activeRequest);
14480                 }
14481                 this.activeRequest = Roo.Ajax.request(o);
14482             }else{
14483                 this.conn.request(o);
14484             }
14485         }else{
14486             callback.call(scope||this, null, arg, false);
14487         }
14488     },
14489
14490     // private
14491     loadResponse : function(o, success, response){
14492         delete this.activeRequest;
14493         if(!success){
14494             this.fireEvent("loadexception", this, o, response);
14495             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14496             return;
14497         }
14498         var result;
14499         try {
14500             result = o.reader.read(response);
14501         }catch(e){
14502             this.fireEvent("loadexception", this, o, response, e);
14503             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14504             return;
14505         }
14506         
14507         this.fireEvent("load", this, o, o.request.arg);
14508         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14509     },
14510
14511     // private
14512     update : function(dataSet){
14513
14514     },
14515
14516     // private
14517     updateResponse : function(dataSet){
14518
14519     }
14520 });/*
14521  * Based on:
14522  * Ext JS Library 1.1.1
14523  * Copyright(c) 2006-2007, Ext JS, LLC.
14524  *
14525  * Originally Released Under LGPL - original licence link has changed is not relivant.
14526  *
14527  * Fork - LGPL
14528  * <script type="text/javascript">
14529  */
14530
14531 /**
14532  * @class Roo.data.ScriptTagProxy
14533  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14534  * other than the originating domain of the running page.<br><br>
14535  * <p>
14536  * <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
14537  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14538  * <p>
14539  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14540  * source code that is used as the source inside a &lt;script> tag.<br><br>
14541  * <p>
14542  * In order for the browser to process the returned data, the server must wrap the data object
14543  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14544  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14545  * depending on whether the callback name was passed:
14546  * <p>
14547  * <pre><code>
14548 boolean scriptTag = false;
14549 String cb = request.getParameter("callback");
14550 if (cb != null) {
14551     scriptTag = true;
14552     response.setContentType("text/javascript");
14553 } else {
14554     response.setContentType("application/x-json");
14555 }
14556 Writer out = response.getWriter();
14557 if (scriptTag) {
14558     out.write(cb + "(");
14559 }
14560 out.print(dataBlock.toJsonString());
14561 if (scriptTag) {
14562     out.write(");");
14563 }
14564 </pre></code>
14565  *
14566  * @constructor
14567  * @param {Object} config A configuration object.
14568  */
14569 Roo.data.ScriptTagProxy = function(config){
14570     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14571     Roo.apply(this, config);
14572     this.head = document.getElementsByTagName("head")[0];
14573 };
14574
14575 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14576
14577 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14578     /**
14579      * @cfg {String} url The URL from which to request the data object.
14580      */
14581     /**
14582      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14583      */
14584     timeout : 30000,
14585     /**
14586      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14587      * the server the name of the callback function set up by the load call to process the returned data object.
14588      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14589      * javascript output which calls this named function passing the data object as its only parameter.
14590      */
14591     callbackParam : "callback",
14592     /**
14593      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14594      * name to the request.
14595      */
14596     nocache : true,
14597
14598     /**
14599      * Load data from the configured URL, read the data object into
14600      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14601      * process that block using the passed callback.
14602      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14603      * for the request to the remote server.
14604      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14605      * object into a block of Roo.data.Records.
14606      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14607      * The function must be passed <ul>
14608      * <li>The Record block object</li>
14609      * <li>The "arg" argument from the load function</li>
14610      * <li>A boolean success indicator</li>
14611      * </ul>
14612      * @param {Object} scope The scope in which to call the callback
14613      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14614      */
14615     load : function(params, reader, callback, scope, arg){
14616         if(this.fireEvent("beforeload", this, params) !== false){
14617
14618             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14619
14620             var url = this.url;
14621             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14622             if(this.nocache){
14623                 url += "&_dc=" + (new Date().getTime());
14624             }
14625             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14626             var trans = {
14627                 id : transId,
14628                 cb : "stcCallback"+transId,
14629                 scriptId : "stcScript"+transId,
14630                 params : params,
14631                 arg : arg,
14632                 url : url,
14633                 callback : callback,
14634                 scope : scope,
14635                 reader : reader
14636             };
14637             var conn = this;
14638
14639             window[trans.cb] = function(o){
14640                 conn.handleResponse(o, trans);
14641             };
14642
14643             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14644
14645             if(this.autoAbort !== false){
14646                 this.abort();
14647             }
14648
14649             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14650
14651             var script = document.createElement("script");
14652             script.setAttribute("src", url);
14653             script.setAttribute("type", "text/javascript");
14654             script.setAttribute("id", trans.scriptId);
14655             this.head.appendChild(script);
14656
14657             this.trans = trans;
14658         }else{
14659             callback.call(scope||this, null, arg, false);
14660         }
14661     },
14662
14663     // private
14664     isLoading : function(){
14665         return this.trans ? true : false;
14666     },
14667
14668     /**
14669      * Abort the current server request.
14670      */
14671     abort : function(){
14672         if(this.isLoading()){
14673             this.destroyTrans(this.trans);
14674         }
14675     },
14676
14677     // private
14678     destroyTrans : function(trans, isLoaded){
14679         this.head.removeChild(document.getElementById(trans.scriptId));
14680         clearTimeout(trans.timeoutId);
14681         if(isLoaded){
14682             window[trans.cb] = undefined;
14683             try{
14684                 delete window[trans.cb];
14685             }catch(e){}
14686         }else{
14687             // if hasn't been loaded, wait for load to remove it to prevent script error
14688             window[trans.cb] = function(){
14689                 window[trans.cb] = undefined;
14690                 try{
14691                     delete window[trans.cb];
14692                 }catch(e){}
14693             };
14694         }
14695     },
14696
14697     // private
14698     handleResponse : function(o, trans){
14699         this.trans = false;
14700         this.destroyTrans(trans, true);
14701         var result;
14702         try {
14703             result = trans.reader.readRecords(o);
14704         }catch(e){
14705             this.fireEvent("loadexception", this, o, trans.arg, e);
14706             trans.callback.call(trans.scope||window, null, trans.arg, false);
14707             return;
14708         }
14709         this.fireEvent("load", this, o, trans.arg);
14710         trans.callback.call(trans.scope||window, result, trans.arg, true);
14711     },
14712
14713     // private
14714     handleFailure : function(trans){
14715         this.trans = false;
14716         this.destroyTrans(trans, false);
14717         this.fireEvent("loadexception", this, null, trans.arg);
14718         trans.callback.call(trans.scope||window, null, trans.arg, false);
14719     }
14720 });/*
14721  * Based on:
14722  * Ext JS Library 1.1.1
14723  * Copyright(c) 2006-2007, Ext JS, LLC.
14724  *
14725  * Originally Released Under LGPL - original licence link has changed is not relivant.
14726  *
14727  * Fork - LGPL
14728  * <script type="text/javascript">
14729  */
14730
14731 /**
14732  * @class Roo.data.JsonReader
14733  * @extends Roo.data.DataReader
14734  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14735  * based on mappings in a provided Roo.data.Record constructor.
14736  * 
14737  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14738  * in the reply previously. 
14739  * 
14740  * <p>
14741  * Example code:
14742  * <pre><code>
14743 var RecordDef = Roo.data.Record.create([
14744     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14745     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14746 ]);
14747 var myReader = new Roo.data.JsonReader({
14748     totalProperty: "results",    // The property which contains the total dataset size (optional)
14749     root: "rows",                // The property which contains an Array of row objects
14750     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14751 }, RecordDef);
14752 </code></pre>
14753  * <p>
14754  * This would consume a JSON file like this:
14755  * <pre><code>
14756 { 'results': 2, 'rows': [
14757     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14758     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14759 }
14760 </code></pre>
14761  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14762  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14763  * paged from the remote server.
14764  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14765  * @cfg {String} root name of the property which contains the Array of row objects.
14766  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14767  * @cfg {Array} fields Array of field definition objects
14768  * @constructor
14769  * Create a new JsonReader
14770  * @param {Object} meta Metadata configuration options
14771  * @param {Object} recordType Either an Array of field definition objects,
14772  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14773  */
14774 Roo.data.JsonReader = function(meta, recordType){
14775     
14776     meta = meta || {};
14777     // set some defaults:
14778     Roo.applyIf(meta, {
14779         totalProperty: 'total',
14780         successProperty : 'success',
14781         root : 'data',
14782         id : 'id'
14783     });
14784     
14785     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14786 };
14787 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14788     
14789     readerType : 'Json',
14790     
14791     /**
14792      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14793      * Used by Store query builder to append _requestMeta to params.
14794      * 
14795      */
14796     metaFromRemote : false,
14797     /**
14798      * This method is only used by a DataProxy which has retrieved data from a remote server.
14799      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14800      * @return {Object} data A data block which is used by an Roo.data.Store object as
14801      * a cache of Roo.data.Records.
14802      */
14803     read : function(response){
14804         var json = response.responseText;
14805        
14806         var o = /* eval:var:o */ eval("("+json+")");
14807         if(!o) {
14808             throw {message: "JsonReader.read: Json object not found"};
14809         }
14810         
14811         if(o.metaData){
14812             
14813             delete this.ef;
14814             this.metaFromRemote = true;
14815             this.meta = o.metaData;
14816             this.recordType = Roo.data.Record.create(o.metaData.fields);
14817             this.onMetaChange(this.meta, this.recordType, o);
14818         }
14819         return this.readRecords(o);
14820     },
14821
14822     // private function a store will implement
14823     onMetaChange : function(meta, recordType, o){
14824
14825     },
14826
14827     /**
14828          * @ignore
14829          */
14830     simpleAccess: function(obj, subsc) {
14831         return obj[subsc];
14832     },
14833
14834         /**
14835          * @ignore
14836          */
14837     getJsonAccessor: function(){
14838         var re = /[\[\.]/;
14839         return function(expr) {
14840             try {
14841                 return(re.test(expr))
14842                     ? new Function("obj", "return obj." + expr)
14843                     : function(obj){
14844                         return obj[expr];
14845                     };
14846             } catch(e){}
14847             return Roo.emptyFn;
14848         };
14849     }(),
14850
14851     /**
14852      * Create a data block containing Roo.data.Records from an XML document.
14853      * @param {Object} o An object which contains an Array of row objects in the property specified
14854      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14855      * which contains the total size of the dataset.
14856      * @return {Object} data A data block which is used by an Roo.data.Store object as
14857      * a cache of Roo.data.Records.
14858      */
14859     readRecords : function(o){
14860         /**
14861          * After any data loads, the raw JSON data is available for further custom processing.
14862          * @type Object
14863          */
14864         this.o = o;
14865         var s = this.meta, Record = this.recordType,
14866             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14867
14868 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14869         if (!this.ef) {
14870             if(s.totalProperty) {
14871                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14872                 }
14873                 if(s.successProperty) {
14874                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14875                 }
14876                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14877                 if (s.id) {
14878                         var g = this.getJsonAccessor(s.id);
14879                         this.getId = function(rec) {
14880                                 var r = g(rec);  
14881                                 return (r === undefined || r === "") ? null : r;
14882                         };
14883                 } else {
14884                         this.getId = function(){return null;};
14885                 }
14886             this.ef = [];
14887             for(var jj = 0; jj < fl; jj++){
14888                 f = fi[jj];
14889                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14890                 this.ef[jj] = this.getJsonAccessor(map);
14891             }
14892         }
14893
14894         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14895         if(s.totalProperty){
14896             var vt = parseInt(this.getTotal(o), 10);
14897             if(!isNaN(vt)){
14898                 totalRecords = vt;
14899             }
14900         }
14901         if(s.successProperty){
14902             var vs = this.getSuccess(o);
14903             if(vs === false || vs === 'false'){
14904                 success = false;
14905             }
14906         }
14907         var records = [];
14908         for(var i = 0; i < c; i++){
14909                 var n = root[i];
14910             var values = {};
14911             var id = this.getId(n);
14912             for(var j = 0; j < fl; j++){
14913                 f = fi[j];
14914             var v = this.ef[j](n);
14915             if (!f.convert) {
14916                 Roo.log('missing convert for ' + f.name);
14917                 Roo.log(f);
14918                 continue;
14919             }
14920             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14921             }
14922             var record = new Record(values, id);
14923             record.json = n;
14924             records[i] = record;
14925         }
14926         return {
14927             raw : o,
14928             success : success,
14929             records : records,
14930             totalRecords : totalRecords
14931         };
14932     },
14933     // used when loading children.. @see loadDataFromChildren
14934     toLoadData: function(rec)
14935     {
14936         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14937         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14938         return { data : data, total : data.length };
14939         
14940     }
14941 });/*
14942  * Based on:
14943  * Ext JS Library 1.1.1
14944  * Copyright(c) 2006-2007, Ext JS, LLC.
14945  *
14946  * Originally Released Under LGPL - original licence link has changed is not relivant.
14947  *
14948  * Fork - LGPL
14949  * <script type="text/javascript">
14950  */
14951
14952 /**
14953  * @class Roo.data.ArrayReader
14954  * @extends Roo.data.DataReader
14955  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14956  * Each element of that Array represents a row of data fields. The
14957  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14958  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14959  * <p>
14960  * Example code:.
14961  * <pre><code>
14962 var RecordDef = Roo.data.Record.create([
14963     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14964     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14965 ]);
14966 var myReader = new Roo.data.ArrayReader({
14967     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14968 }, RecordDef);
14969 </code></pre>
14970  * <p>
14971  * This would consume an Array like this:
14972  * <pre><code>
14973 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14974   </code></pre>
14975  
14976  * @constructor
14977  * Create a new JsonReader
14978  * @param {Object} meta Metadata configuration options.
14979  * @param {Object|Array} recordType Either an Array of field definition objects
14980  * 
14981  * @cfg {Array} fields Array of field definition objects
14982  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14983  * as specified to {@link Roo.data.Record#create},
14984  * or an {@link Roo.data.Record} object
14985  *
14986  * 
14987  * created using {@link Roo.data.Record#create}.
14988  */
14989 Roo.data.ArrayReader = function(meta, recordType)
14990 {    
14991     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14992 };
14993
14994 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14995     
14996       /**
14997      * Create a data block containing Roo.data.Records from an XML document.
14998      * @param {Object} o An Array of row objects which represents the dataset.
14999      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15000      * a cache of Roo.data.Records.
15001      */
15002     readRecords : function(o)
15003     {
15004         var sid = this.meta ? this.meta.id : null;
15005         var recordType = this.recordType, fields = recordType.prototype.fields;
15006         var records = [];
15007         var root = o;
15008         for(var i = 0; i < root.length; i++){
15009             var n = root[i];
15010             var values = {};
15011             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15012             for(var j = 0, jlen = fields.length; j < jlen; j++){
15013                 var f = fields.items[j];
15014                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15015                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15016                 v = f.convert(v);
15017                 values[f.name] = v;
15018             }
15019             var record = new recordType(values, id);
15020             record.json = n;
15021             records[records.length] = record;
15022         }
15023         return {
15024             records : records,
15025             totalRecords : records.length
15026         };
15027     },
15028     // used when loading children.. @see loadDataFromChildren
15029     toLoadData: function(rec)
15030     {
15031         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15032         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15033         
15034     }
15035     
15036     
15037 });/*
15038  * - LGPL
15039  * * 
15040  */
15041
15042 /**
15043  * @class Roo.bootstrap.ComboBox
15044  * @extends Roo.bootstrap.TriggerField
15045  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15046  * @cfg {Boolean} append (true|false) default false
15047  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15048  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15049  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15050  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15051  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15052  * @cfg {Boolean} animate default true
15053  * @cfg {Boolean} emptyResultText only for touch device
15054  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15055  * @cfg {String} emptyTitle default ''
15056  * @cfg {Number} width fixed with? experimental
15057  * @constructor
15058  * Create a new ComboBox.
15059  * @param {Object} config Configuration options
15060  */
15061 Roo.bootstrap.ComboBox = function(config){
15062     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15063     this.addEvents({
15064         /**
15065          * @event expand
15066          * Fires when the dropdown list is expanded
15067         * @param {Roo.bootstrap.ComboBox} combo This combo box
15068         */
15069         'expand' : true,
15070         /**
15071          * @event collapse
15072          * Fires when the dropdown list is collapsed
15073         * @param {Roo.bootstrap.ComboBox} combo This combo box
15074         */
15075         'collapse' : true,
15076         /**
15077          * @event beforeselect
15078          * Fires before a list item is selected. Return false to cancel the selection.
15079         * @param {Roo.bootstrap.ComboBox} combo This combo box
15080         * @param {Roo.data.Record} record The data record returned from the underlying store
15081         * @param {Number} index The index of the selected item in the dropdown list
15082         */
15083         'beforeselect' : true,
15084         /**
15085          * @event select
15086          * Fires when a list item is selected
15087         * @param {Roo.bootstrap.ComboBox} combo This combo box
15088         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15089         * @param {Number} index The index of the selected item in the dropdown list
15090         */
15091         'select' : true,
15092         /**
15093          * @event beforequery
15094          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15095          * The event object passed has these properties:
15096         * @param {Roo.bootstrap.ComboBox} combo This combo box
15097         * @param {String} query The query
15098         * @param {Boolean} forceAll true to force "all" query
15099         * @param {Boolean} cancel true to cancel the query
15100         * @param {Object} e The query event object
15101         */
15102         'beforequery': true,
15103          /**
15104          * @event add
15105          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15106         * @param {Roo.bootstrap.ComboBox} combo This combo box
15107         */
15108         'add' : true,
15109         /**
15110          * @event edit
15111          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15112         * @param {Roo.bootstrap.ComboBox} combo This combo box
15113         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15114         */
15115         'edit' : true,
15116         /**
15117          * @event remove
15118          * Fires when the remove value from the combobox array
15119         * @param {Roo.bootstrap.ComboBox} combo This combo box
15120         */
15121         'remove' : true,
15122         /**
15123          * @event afterremove
15124          * Fires when the remove value from the combobox array
15125         * @param {Roo.bootstrap.ComboBox} combo This combo box
15126         */
15127         'afterremove' : true,
15128         /**
15129          * @event specialfilter
15130          * Fires when specialfilter
15131             * @param {Roo.bootstrap.ComboBox} combo This combo box
15132             */
15133         'specialfilter' : true,
15134         /**
15135          * @event tick
15136          * Fires when tick the element
15137             * @param {Roo.bootstrap.ComboBox} combo This combo box
15138             */
15139         'tick' : true,
15140         /**
15141          * @event touchviewdisplay
15142          * Fires when touch view require special display (default is using displayField)
15143             * @param {Roo.bootstrap.ComboBox} combo This combo box
15144             * @param {Object} cfg set html .
15145             */
15146         'touchviewdisplay' : true
15147         
15148     });
15149     
15150     this.item = [];
15151     this.tickItems = [];
15152     
15153     this.selectedIndex = -1;
15154     if(this.mode == 'local'){
15155         if(config.queryDelay === undefined){
15156             this.queryDelay = 10;
15157         }
15158         if(config.minChars === undefined){
15159             this.minChars = 0;
15160         }
15161     }
15162 };
15163
15164 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15165      
15166     /**
15167      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15168      * rendering into an Roo.Editor, defaults to false)
15169      */
15170     /**
15171      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15172      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15173      */
15174     /**
15175      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15176      */
15177     /**
15178      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15179      * the dropdown list (defaults to undefined, with no header element)
15180      */
15181
15182      /**
15183      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15184      */
15185      
15186      /**
15187      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15188      */
15189     listWidth: undefined,
15190     /**
15191      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15192      * mode = 'remote' or 'text' if mode = 'local')
15193      */
15194     displayField: undefined,
15195     
15196     /**
15197      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15198      * mode = 'remote' or 'value' if mode = 'local'). 
15199      * Note: use of a valueField requires the user make a selection
15200      * in order for a value to be mapped.
15201      */
15202     valueField: undefined,
15203     /**
15204      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15205      */
15206     modalTitle : '',
15207     
15208     /**
15209      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15210      * field's data value (defaults to the underlying DOM element's name)
15211      */
15212     hiddenName: undefined,
15213     /**
15214      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15215      */
15216     listClass: '',
15217     /**
15218      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15219      */
15220     selectedClass: 'active',
15221     
15222     /**
15223      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15224      */
15225     shadow:'sides',
15226     /**
15227      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15228      * anchor positions (defaults to 'tl-bl')
15229      */
15230     listAlign: 'tl-bl?',
15231     /**
15232      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15233      */
15234     maxHeight: 300,
15235     /**
15236      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15237      * query specified by the allQuery config option (defaults to 'query')
15238      */
15239     triggerAction: 'query',
15240     /**
15241      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15242      * (defaults to 4, does not apply if editable = false)
15243      */
15244     minChars : 4,
15245     /**
15246      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15247      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15248      */
15249     typeAhead: false,
15250     /**
15251      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15252      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15253      */
15254     queryDelay: 500,
15255     /**
15256      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15257      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15258      */
15259     pageSize: 0,
15260     /**
15261      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15262      * when editable = true (defaults to false)
15263      */
15264     selectOnFocus:false,
15265     /**
15266      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15267      */
15268     queryParam: 'query',
15269     /**
15270      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15271      * when mode = 'remote' (defaults to 'Loading...')
15272      */
15273     loadingText: 'Loading...',
15274     /**
15275      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15276      */
15277     resizable: false,
15278     /**
15279      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15280      */
15281     handleHeight : 8,
15282     /**
15283      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15284      * traditional select (defaults to true)
15285      */
15286     editable: true,
15287     /**
15288      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15289      */
15290     allQuery: '',
15291     /**
15292      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15293      */
15294     mode: 'remote',
15295     /**
15296      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15297      * listWidth has a higher value)
15298      */
15299     minListWidth : 70,
15300     /**
15301      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15302      * allow the user to set arbitrary text into the field (defaults to false)
15303      */
15304     forceSelection:false,
15305     /**
15306      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15307      * if typeAhead = true (defaults to 250)
15308      */
15309     typeAheadDelay : 250,
15310     /**
15311      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15312      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15313      */
15314     valueNotFoundText : undefined,
15315     /**
15316      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15317      */
15318     blockFocus : false,
15319     
15320     /**
15321      * @cfg {Boolean} disableClear Disable showing of clear button.
15322      */
15323     disableClear : false,
15324     /**
15325      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15326      */
15327     alwaysQuery : false,
15328     
15329     /**
15330      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15331      */
15332     multiple : false,
15333     
15334     /**
15335      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15336      */
15337     invalidClass : "has-warning",
15338     
15339     /**
15340      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15341      */
15342     validClass : "has-success",
15343     
15344     /**
15345      * @cfg {Boolean} specialFilter (true|false) special filter default false
15346      */
15347     specialFilter : false,
15348     
15349     /**
15350      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15351      */
15352     mobileTouchView : true,
15353     
15354     /**
15355      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15356      */
15357     useNativeIOS : false,
15358     
15359     /**
15360      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15361      */
15362     mobile_restrict_height : false,
15363     
15364     ios_options : false,
15365     
15366     //private
15367     addicon : false,
15368     editicon: false,
15369     
15370     page: 0,
15371     hasQuery: false,
15372     append: false,
15373     loadNext: false,
15374     autoFocus : true,
15375     tickable : false,
15376     btnPosition : 'right',
15377     triggerList : true,
15378     showToggleBtn : true,
15379     animate : true,
15380     emptyResultText: 'Empty',
15381     triggerText : 'Select',
15382     emptyTitle : '',
15383     width : false,
15384     
15385     // element that contains real text value.. (when hidden is used..)
15386     
15387     getAutoCreate : function()
15388     {   
15389         var cfg = false;
15390         //render
15391         /*
15392          * Render classic select for iso
15393          */
15394         
15395         if(Roo.isIOS && this.useNativeIOS){
15396             cfg = this.getAutoCreateNativeIOS();
15397             return cfg;
15398         }
15399         
15400         /*
15401          * Touch Devices
15402          */
15403         
15404         if(Roo.isTouch && this.mobileTouchView){
15405             cfg = this.getAutoCreateTouchView();
15406             return cfg;;
15407         }
15408         
15409         /*
15410          *  Normal ComboBox
15411          */
15412         if(!this.tickable){
15413             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15414             return cfg;
15415         }
15416         
15417         /*
15418          *  ComboBox with tickable selections
15419          */
15420              
15421         var align = this.labelAlign || this.parentLabelAlign();
15422         
15423         cfg = {
15424             cls : 'form-group roo-combobox-tickable' //input-group
15425         };
15426         
15427         var btn_text_select = '';
15428         var btn_text_done = '';
15429         var btn_text_cancel = '';
15430         
15431         if (this.btn_text_show) {
15432             btn_text_select = 'Select';
15433             btn_text_done = 'Done';
15434             btn_text_cancel = 'Cancel'; 
15435         }
15436         
15437         var buttons = {
15438             tag : 'div',
15439             cls : 'tickable-buttons',
15440             cn : [
15441                 {
15442                     tag : 'button',
15443                     type : 'button',
15444                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15445                     //html : this.triggerText
15446                     html: btn_text_select
15447                 },
15448                 {
15449                     tag : 'button',
15450                     type : 'button',
15451                     name : 'ok',
15452                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15453                     //html : 'Done'
15454                     html: btn_text_done
15455                 },
15456                 {
15457                     tag : 'button',
15458                     type : 'button',
15459                     name : 'cancel',
15460                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15461                     //html : 'Cancel'
15462                     html: btn_text_cancel
15463                 }
15464             ]
15465         };
15466         
15467         if(this.editable){
15468             buttons.cn.unshift({
15469                 tag: 'input',
15470                 cls: 'roo-select2-search-field-input'
15471             });
15472         }
15473         
15474         var _this = this;
15475         
15476         Roo.each(buttons.cn, function(c){
15477             if (_this.size) {
15478                 c.cls += ' btn-' + _this.size;
15479             }
15480
15481             if (_this.disabled) {
15482                 c.disabled = true;
15483             }
15484         });
15485         
15486         var box = {
15487             tag: 'div',
15488             style : 'display: contents',
15489             cn: [
15490                 {
15491                     tag: 'input',
15492                     type : 'hidden',
15493                     cls: 'form-hidden-field'
15494                 },
15495                 {
15496                     tag: 'ul',
15497                     cls: 'roo-select2-choices',
15498                     cn:[
15499                         {
15500                             tag: 'li',
15501                             cls: 'roo-select2-search-field',
15502                             cn: [
15503                                 buttons
15504                             ]
15505                         }
15506                     ]
15507                 }
15508             ]
15509         };
15510         
15511         var combobox = {
15512             cls: 'roo-select2-container input-group roo-select2-container-multi',
15513             cn: [
15514                 
15515                 box
15516 //                {
15517 //                    tag: 'ul',
15518 //                    cls: 'typeahead typeahead-long dropdown-menu',
15519 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15520 //                }
15521             ]
15522         };
15523         
15524         if(this.hasFeedback && !this.allowBlank){
15525             
15526             var feedback = {
15527                 tag: 'span',
15528                 cls: 'glyphicon form-control-feedback'
15529             };
15530
15531             combobox.cn.push(feedback);
15532         }
15533         
15534         
15535         
15536         var indicator = {
15537             tag : 'i',
15538             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15539             tooltip : 'This field is required'
15540         };
15541         if (Roo.bootstrap.version == 4) {
15542             indicator = {
15543                 tag : 'i',
15544                 style : 'display:none'
15545             };
15546         }
15547         if (align ==='left' && this.fieldLabel.length) {
15548             
15549             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15550             
15551             cfg.cn = [
15552                 indicator,
15553                 {
15554                     tag: 'label',
15555                     'for' :  id,
15556                     cls : 'control-label col-form-label',
15557                     html : this.fieldLabel
15558
15559                 },
15560                 {
15561                     cls : "", 
15562                     cn: [
15563                         combobox
15564                     ]
15565                 }
15566
15567             ];
15568             
15569             var labelCfg = cfg.cn[1];
15570             var contentCfg = cfg.cn[2];
15571             
15572
15573             if(this.indicatorpos == 'right'){
15574                 
15575                 cfg.cn = [
15576                     {
15577                         tag: 'label',
15578                         'for' :  id,
15579                         cls : 'control-label col-form-label',
15580                         cn : [
15581                             {
15582                                 tag : 'span',
15583                                 html : this.fieldLabel
15584                             },
15585                             indicator
15586                         ]
15587                     },
15588                     {
15589                         cls : "",
15590                         cn: [
15591                             combobox
15592                         ]
15593                     }
15594
15595                 ];
15596                 
15597                 
15598                 
15599                 labelCfg = cfg.cn[0];
15600                 contentCfg = cfg.cn[1];
15601             
15602             }
15603             
15604             if(this.labelWidth > 12){
15605                 labelCfg.style = "width: " + this.labelWidth + 'px';
15606             }
15607             if(this.width * 1 > 0){
15608                 contentCfg.style = "width: " + this.width + 'px';
15609             }
15610             if(this.labelWidth < 13 && this.labelmd == 0){
15611                 this.labelmd = this.labelWidth;
15612             }
15613             
15614             if(this.labellg > 0){
15615                 labelCfg.cls += ' col-lg-' + this.labellg;
15616                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15617             }
15618             
15619             if(this.labelmd > 0){
15620                 labelCfg.cls += ' col-md-' + this.labelmd;
15621                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15622             }
15623             
15624             if(this.labelsm > 0){
15625                 labelCfg.cls += ' col-sm-' + this.labelsm;
15626                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15627             }
15628             
15629             if(this.labelxs > 0){
15630                 labelCfg.cls += ' col-xs-' + this.labelxs;
15631                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15632             }
15633                 
15634                 
15635         } else if ( this.fieldLabel.length) {
15636 //                Roo.log(" label");
15637                  cfg.cn = [
15638                    indicator,
15639                     {
15640                         tag: 'label',
15641                         //cls : 'input-group-addon',
15642                         html : this.fieldLabel
15643                     },
15644                     combobox
15645                 ];
15646                 
15647                 if(this.indicatorpos == 'right'){
15648                     cfg.cn = [
15649                         {
15650                             tag: 'label',
15651                             //cls : 'input-group-addon',
15652                             html : this.fieldLabel
15653                         },
15654                         indicator,
15655                         combobox
15656                     ];
15657                     
15658                 }
15659
15660         } else {
15661             
15662 //                Roo.log(" no label && no align");
15663                 cfg = combobox
15664                      
15665                 
15666         }
15667          
15668         var settings=this;
15669         ['xs','sm','md','lg'].map(function(size){
15670             if (settings[size]) {
15671                 cfg.cls += ' col-' + size + '-' + settings[size];
15672             }
15673         });
15674         
15675         return cfg;
15676         
15677     },
15678     
15679     _initEventsCalled : false,
15680     
15681     // private
15682     initEvents: function()
15683     {   
15684         if (this._initEventsCalled) { // as we call render... prevent looping...
15685             return;
15686         }
15687         this._initEventsCalled = true;
15688         
15689         if (!this.store) {
15690             throw "can not find store for combo";
15691         }
15692         
15693         this.indicator = this.indicatorEl();
15694         
15695         this.store = Roo.factory(this.store, Roo.data);
15696         this.store.parent = this;
15697         
15698         // if we are building from html. then this element is so complex, that we can not really
15699         // use the rendered HTML.
15700         // so we have to trash and replace the previous code.
15701         if (Roo.XComponent.build_from_html) {
15702             // remove this element....
15703             var e = this.el.dom, k=0;
15704             while (e ) { e = e.previousSibling;  ++k;}
15705
15706             this.el.remove();
15707             
15708             this.el=false;
15709             this.rendered = false;
15710             
15711             this.render(this.parent().getChildContainer(true), k);
15712         }
15713         
15714         if(Roo.isIOS && this.useNativeIOS){
15715             this.initIOSView();
15716             return;
15717         }
15718         
15719         /*
15720          * Touch Devices
15721          */
15722         
15723         if(Roo.isTouch && this.mobileTouchView){
15724             this.initTouchView();
15725             return;
15726         }
15727         
15728         if(this.tickable){
15729             this.initTickableEvents();
15730             return;
15731         }
15732         
15733         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15734         
15735         if(this.hiddenName){
15736             
15737             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15738             
15739             this.hiddenField.dom.value =
15740                 this.hiddenValue !== undefined ? this.hiddenValue :
15741                 this.value !== undefined ? this.value : '';
15742
15743             // prevent input submission
15744             this.el.dom.removeAttribute('name');
15745             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15746              
15747              
15748         }
15749         //if(Roo.isGecko){
15750         //    this.el.dom.setAttribute('autocomplete', 'off');
15751         //}
15752         
15753         var cls = 'x-combo-list';
15754         
15755         //this.list = new Roo.Layer({
15756         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15757         //});
15758         
15759         var _this = this;
15760         
15761         (function(){
15762             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15763             _this.list.setWidth(lw);
15764         }).defer(100);
15765         
15766         this.list.on('mouseover', this.onViewOver, this);
15767         this.list.on('mousemove', this.onViewMove, this);
15768         this.list.on('scroll', this.onViewScroll, this);
15769         
15770         /*
15771         this.list.swallowEvent('mousewheel');
15772         this.assetHeight = 0;
15773
15774         if(this.title){
15775             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15776             this.assetHeight += this.header.getHeight();
15777         }
15778
15779         this.innerList = this.list.createChild({cls:cls+'-inner'});
15780         this.innerList.on('mouseover', this.onViewOver, this);
15781         this.innerList.on('mousemove', this.onViewMove, this);
15782         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15783         
15784         if(this.allowBlank && !this.pageSize && !this.disableClear){
15785             this.footer = this.list.createChild({cls:cls+'-ft'});
15786             this.pageTb = new Roo.Toolbar(this.footer);
15787            
15788         }
15789         if(this.pageSize){
15790             this.footer = this.list.createChild({cls:cls+'-ft'});
15791             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15792                     {pageSize: this.pageSize});
15793             
15794         }
15795         
15796         if (this.pageTb && this.allowBlank && !this.disableClear) {
15797             var _this = this;
15798             this.pageTb.add(new Roo.Toolbar.Fill(), {
15799                 cls: 'x-btn-icon x-btn-clear',
15800                 text: '&#160;',
15801                 handler: function()
15802                 {
15803                     _this.collapse();
15804                     _this.clearValue();
15805                     _this.onSelect(false, -1);
15806                 }
15807             });
15808         }
15809         if (this.footer) {
15810             this.assetHeight += this.footer.getHeight();
15811         }
15812         */
15813             
15814         if(!this.tpl){
15815             this.tpl = Roo.bootstrap.version == 4 ?
15816                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15817                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15818         }
15819
15820         this.view = new Roo.View(this.list, this.tpl, {
15821             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15822         });
15823         //this.view.wrapEl.setDisplayed(false);
15824         this.view.on('click', this.onViewClick, this);
15825         
15826         
15827         this.store.on('beforeload', this.onBeforeLoad, this);
15828         this.store.on('load', this.onLoad, this);
15829         this.store.on('loadexception', this.onLoadException, this);
15830         /*
15831         if(this.resizable){
15832             this.resizer = new Roo.Resizable(this.list,  {
15833                pinned:true, handles:'se'
15834             });
15835             this.resizer.on('resize', function(r, w, h){
15836                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15837                 this.listWidth = w;
15838                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15839                 this.restrictHeight();
15840             }, this);
15841             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15842         }
15843         */
15844         if(!this.editable){
15845             this.editable = true;
15846             this.setEditable(false);
15847         }
15848         
15849         /*
15850         
15851         if (typeof(this.events.add.listeners) != 'undefined') {
15852             
15853             this.addicon = this.wrap.createChild(
15854                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15855        
15856             this.addicon.on('click', function(e) {
15857                 this.fireEvent('add', this);
15858             }, this);
15859         }
15860         if (typeof(this.events.edit.listeners) != 'undefined') {
15861             
15862             this.editicon = this.wrap.createChild(
15863                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15864             if (this.addicon) {
15865                 this.editicon.setStyle('margin-left', '40px');
15866             }
15867             this.editicon.on('click', function(e) {
15868                 
15869                 // we fire even  if inothing is selected..
15870                 this.fireEvent('edit', this, this.lastData );
15871                 
15872             }, this);
15873         }
15874         */
15875         
15876         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15877             "up" : function(e){
15878                 this.inKeyMode = true;
15879                 this.selectPrev();
15880             },
15881
15882             "down" : function(e){
15883                 if(!this.isExpanded()){
15884                     this.onTriggerClick();
15885                 }else{
15886                     this.inKeyMode = true;
15887                     this.selectNext();
15888                 }
15889             },
15890
15891             "enter" : function(e){
15892 //                this.onViewClick();
15893                 //return true;
15894                 this.collapse();
15895                 
15896                 if(this.fireEvent("specialkey", this, e)){
15897                     this.onViewClick(false);
15898                 }
15899                 
15900                 return true;
15901             },
15902
15903             "esc" : function(e){
15904                 this.collapse();
15905             },
15906
15907             "tab" : function(e){
15908                 this.collapse();
15909                 
15910                 if(this.fireEvent("specialkey", this, e)){
15911                     this.onViewClick(false);
15912                 }
15913                 
15914                 return true;
15915             },
15916
15917             scope : this,
15918
15919             doRelay : function(foo, bar, hname){
15920                 if(hname == 'down' || this.scope.isExpanded()){
15921                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15922                 }
15923                 return true;
15924             },
15925
15926             forceKeyDown: true
15927         });
15928         
15929         
15930         this.queryDelay = Math.max(this.queryDelay || 10,
15931                 this.mode == 'local' ? 10 : 250);
15932         
15933         
15934         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15935         
15936         if(this.typeAhead){
15937             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15938         }
15939         if(this.editable !== false){
15940             this.inputEl().on("keyup", this.onKeyUp, this);
15941         }
15942         if(this.forceSelection){
15943             this.inputEl().on('blur', this.doForce, this);
15944         }
15945         
15946         if(this.multiple){
15947             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15948             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15949         }
15950     },
15951     
15952     initTickableEvents: function()
15953     {   
15954         this.createList();
15955         
15956         if(this.hiddenName){
15957             
15958             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15959             
15960             this.hiddenField.dom.value =
15961                 this.hiddenValue !== undefined ? this.hiddenValue :
15962                 this.value !== undefined ? this.value : '';
15963
15964             // prevent input submission
15965             this.el.dom.removeAttribute('name');
15966             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15967              
15968              
15969         }
15970         
15971 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15972         
15973         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15974         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15975         if(this.triggerList){
15976             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15977         }
15978          
15979         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15980         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15981         
15982         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15983         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15984         
15985         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15986         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15987         
15988         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15989         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15990         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15991         
15992         this.okBtn.hide();
15993         this.cancelBtn.hide();
15994         
15995         var _this = this;
15996         
15997         (function(){
15998             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15999             _this.list.setWidth(lw);
16000         }).defer(100);
16001         
16002         this.list.on('mouseover', this.onViewOver, this);
16003         this.list.on('mousemove', this.onViewMove, this);
16004         
16005         this.list.on('scroll', this.onViewScroll, this);
16006         
16007         if(!this.tpl){
16008             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16009                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16010         }
16011
16012         this.view = new Roo.View(this.list, this.tpl, {
16013             singleSelect:true,
16014             tickable:true,
16015             parent:this,
16016             store: this.store,
16017             selectedClass: this.selectedClass
16018         });
16019         
16020         //this.view.wrapEl.setDisplayed(false);
16021         this.view.on('click', this.onViewClick, this);
16022         
16023         
16024         
16025         this.store.on('beforeload', this.onBeforeLoad, this);
16026         this.store.on('load', this.onLoad, this);
16027         this.store.on('loadexception', this.onLoadException, this);
16028         
16029         if(this.editable){
16030             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16031                 "up" : function(e){
16032                     this.inKeyMode = true;
16033                     this.selectPrev();
16034                 },
16035
16036                 "down" : function(e){
16037                     this.inKeyMode = true;
16038                     this.selectNext();
16039                 },
16040
16041                 "enter" : function(e){
16042                     if(this.fireEvent("specialkey", this, e)){
16043                         this.onViewClick(false);
16044                     }
16045                     
16046                     return true;
16047                 },
16048
16049                 "esc" : function(e){
16050                     this.onTickableFooterButtonClick(e, false, false);
16051                 },
16052
16053                 "tab" : function(e){
16054                     this.fireEvent("specialkey", this, e);
16055                     
16056                     this.onTickableFooterButtonClick(e, false, false);
16057                     
16058                     return true;
16059                 },
16060
16061                 scope : this,
16062
16063                 doRelay : function(e, fn, key){
16064                     if(this.scope.isExpanded()){
16065                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16066                     }
16067                     return true;
16068                 },
16069
16070                 forceKeyDown: true
16071             });
16072         }
16073         
16074         this.queryDelay = Math.max(this.queryDelay || 10,
16075                 this.mode == 'local' ? 10 : 250);
16076         
16077         
16078         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16079         
16080         if(this.typeAhead){
16081             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16082         }
16083         
16084         if(this.editable !== false){
16085             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16086         }
16087         
16088         this.indicator = this.indicatorEl();
16089         
16090         if(this.indicator){
16091             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16092             this.indicator.hide();
16093         }
16094         
16095     },
16096
16097     onDestroy : function(){
16098         if(this.view){
16099             this.view.setStore(null);
16100             this.view.el.removeAllListeners();
16101             this.view.el.remove();
16102             this.view.purgeListeners();
16103         }
16104         if(this.list){
16105             this.list.dom.innerHTML  = '';
16106         }
16107         
16108         if(this.store){
16109             this.store.un('beforeload', this.onBeforeLoad, this);
16110             this.store.un('load', this.onLoad, this);
16111             this.store.un('loadexception', this.onLoadException, this);
16112         }
16113         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16114     },
16115
16116     // private
16117     fireKey : function(e){
16118         if(e.isNavKeyPress() && !this.list.isVisible()){
16119             this.fireEvent("specialkey", this, e);
16120         }
16121     },
16122
16123     // private
16124     onResize: function(w, h)
16125     {
16126         
16127         
16128 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16129 //        
16130 //        if(typeof w != 'number'){
16131 //            // we do not handle it!?!?
16132 //            return;
16133 //        }
16134 //        var tw = this.trigger.getWidth();
16135 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16136 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16137 //        var x = w - tw;
16138 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16139 //            
16140 //        //this.trigger.setStyle('left', x+'px');
16141 //        
16142 //        if(this.list && this.listWidth === undefined){
16143 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16144 //            this.list.setWidth(lw);
16145 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16146 //        }
16147         
16148     
16149         
16150     },
16151
16152     /**
16153      * Allow or prevent the user from directly editing the field text.  If false is passed,
16154      * the user will only be able to select from the items defined in the dropdown list.  This method
16155      * is the runtime equivalent of setting the 'editable' config option at config time.
16156      * @param {Boolean} value True to allow the user to directly edit the field text
16157      */
16158     setEditable : function(value){
16159         if(value == this.editable){
16160             return;
16161         }
16162         this.editable = value;
16163         if(!value){
16164             this.inputEl().dom.setAttribute('readOnly', true);
16165             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16166             this.inputEl().addClass('x-combo-noedit');
16167         }else{
16168             this.inputEl().dom.setAttribute('readOnly', false);
16169             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16170             this.inputEl().removeClass('x-combo-noedit');
16171         }
16172     },
16173
16174     // private
16175     
16176     onBeforeLoad : function(combo,opts){
16177         if(!this.hasFocus){
16178             return;
16179         }
16180          if (!opts.add) {
16181             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16182          }
16183         this.restrictHeight();
16184         this.selectedIndex = -1;
16185     },
16186
16187     // private
16188     onLoad : function(){
16189         
16190         this.hasQuery = false;
16191         
16192         if(!this.hasFocus){
16193             return;
16194         }
16195         
16196         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16197             this.loading.hide();
16198         }
16199         
16200         if(this.store.getCount() > 0){
16201             
16202             this.expand();
16203             this.restrictHeight();
16204             if(this.lastQuery == this.allQuery){
16205                 if(this.editable && !this.tickable){
16206                     this.inputEl().dom.select();
16207                 }
16208                 
16209                 if(
16210                     !this.selectByValue(this.value, true) &&
16211                     this.autoFocus && 
16212                     (
16213                         !this.store.lastOptions ||
16214                         typeof(this.store.lastOptions.add) == 'undefined' || 
16215                         this.store.lastOptions.add != true
16216                     )
16217                 ){
16218                     this.select(0, true);
16219                 }
16220             }else{
16221                 if(this.autoFocus){
16222                     this.selectNext();
16223                 }
16224                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16225                     this.taTask.delay(this.typeAheadDelay);
16226                 }
16227             }
16228         }else{
16229             this.onEmptyResults();
16230         }
16231         
16232         //this.el.focus();
16233     },
16234     // private
16235     onLoadException : function()
16236     {
16237         this.hasQuery = false;
16238         
16239         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16240             this.loading.hide();
16241         }
16242         
16243         if(this.tickable && this.editable){
16244             return;
16245         }
16246         
16247         this.collapse();
16248         // only causes errors at present
16249         //Roo.log(this.store.reader.jsonData);
16250         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16251             // fixme
16252             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16253         //}
16254         
16255         
16256     },
16257     // private
16258     onTypeAhead : function(){
16259         if(this.store.getCount() > 0){
16260             var r = this.store.getAt(0);
16261             var newValue = r.data[this.displayField];
16262             var len = newValue.length;
16263             var selStart = this.getRawValue().length;
16264             
16265             if(selStart != len){
16266                 this.setRawValue(newValue);
16267                 this.selectText(selStart, newValue.length);
16268             }
16269         }
16270     },
16271
16272     // private
16273     onSelect : function(record, index){
16274         
16275         if(this.fireEvent('beforeselect', this, record, index) !== false){
16276         
16277             this.setFromData(index > -1 ? record.data : false);
16278             
16279             this.collapse();
16280             this.fireEvent('select', this, record, index);
16281         }
16282     },
16283
16284     /**
16285      * Returns the currently selected field value or empty string if no value is set.
16286      * @return {String} value The selected value
16287      */
16288     getValue : function()
16289     {
16290         if(Roo.isIOS && this.useNativeIOS){
16291             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16292         }
16293         
16294         if(this.multiple){
16295             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16296         }
16297         
16298         if(this.valueField){
16299             return typeof this.value != 'undefined' ? this.value : '';
16300         }else{
16301             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16302         }
16303     },
16304     
16305     getRawValue : function()
16306     {
16307         if(Roo.isIOS && this.useNativeIOS){
16308             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16309         }
16310         
16311         var v = this.inputEl().getValue();
16312         
16313         return v;
16314     },
16315
16316     /**
16317      * Clears any text/value currently set in the field
16318      */
16319     clearValue : function(){
16320         
16321         if(this.hiddenField){
16322             this.hiddenField.dom.value = '';
16323         }
16324         this.value = '';
16325         this.setRawValue('');
16326         this.lastSelectionText = '';
16327         this.lastData = false;
16328         
16329         var close = this.closeTriggerEl();
16330         
16331         if(close){
16332             close.hide();
16333         }
16334         
16335         this.validate();
16336         
16337     },
16338
16339     /**
16340      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16341      * will be displayed in the field.  If the value does not match the data value of an existing item,
16342      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16343      * Otherwise the field will be blank (although the value will still be set).
16344      * @param {String} value The value to match
16345      */
16346     setValue : function(v)
16347     {
16348         if(Roo.isIOS && this.useNativeIOS){
16349             this.setIOSValue(v);
16350             return;
16351         }
16352         
16353         if(this.multiple){
16354             this.syncValue();
16355             return;
16356         }
16357         
16358         var text = v;
16359         if(this.valueField){
16360             var r = this.findRecord(this.valueField, v);
16361             if(r){
16362                 text = r.data[this.displayField];
16363             }else if(this.valueNotFoundText !== undefined){
16364                 text = this.valueNotFoundText;
16365             }
16366         }
16367         this.lastSelectionText = text;
16368         if(this.hiddenField){
16369             this.hiddenField.dom.value = v;
16370         }
16371         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16372         this.value = v;
16373         
16374         var close = this.closeTriggerEl();
16375         
16376         if(close){
16377             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16378         }
16379         
16380         this.validate();
16381     },
16382     /**
16383      * @property {Object} the last set data for the element
16384      */
16385     
16386     lastData : false,
16387     /**
16388      * Sets the value of the field based on a object which is related to the record format for the store.
16389      * @param {Object} value the value to set as. or false on reset?
16390      */
16391     setFromData : function(o){
16392         
16393         if(this.multiple){
16394             this.addItem(o);
16395             return;
16396         }
16397             
16398         var dv = ''; // display value
16399         var vv = ''; // value value..
16400         this.lastData = o;
16401         if (this.displayField) {
16402             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16403         } else {
16404             // this is an error condition!!!
16405             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16406         }
16407         
16408         if(this.valueField){
16409             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16410         }
16411         
16412         var close = this.closeTriggerEl();
16413         
16414         if(close){
16415             if(dv.length || vv * 1 > 0){
16416                 close.show() ;
16417                 this.blockFocus=true;
16418             } else {
16419                 close.hide();
16420             }             
16421         }
16422         
16423         if(this.hiddenField){
16424             this.hiddenField.dom.value = vv;
16425             
16426             this.lastSelectionText = dv;
16427             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16428             this.value = vv;
16429             return;
16430         }
16431         // no hidden field.. - we store the value in 'value', but still display
16432         // display field!!!!
16433         this.lastSelectionText = dv;
16434         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16435         this.value = vv;
16436         
16437         
16438         
16439     },
16440     // private
16441     reset : function(){
16442         // overridden so that last data is reset..
16443         
16444         if(this.multiple){
16445             this.clearItem();
16446             return;
16447         }
16448         
16449         this.setValue(this.originalValue);
16450         //this.clearInvalid();
16451         this.lastData = false;
16452         if (this.view) {
16453             this.view.clearSelections();
16454         }
16455         
16456         this.validate();
16457     },
16458     // private
16459     findRecord : function(prop, value){
16460         var record;
16461         if(this.store.getCount() > 0){
16462             this.store.each(function(r){
16463                 if(r.data[prop] == value){
16464                     record = r;
16465                     return false;
16466                 }
16467                 return true;
16468             });
16469         }
16470         return record;
16471     },
16472     
16473     getName: function()
16474     {
16475         // returns hidden if it's set..
16476         if (!this.rendered) {return ''};
16477         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16478         
16479     },
16480     // private
16481     onViewMove : function(e, t){
16482         this.inKeyMode = false;
16483     },
16484
16485     // private
16486     onViewOver : function(e, t){
16487         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16488             return;
16489         }
16490         var item = this.view.findItemFromChild(t);
16491         
16492         if(item){
16493             var index = this.view.indexOf(item);
16494             this.select(index, false);
16495         }
16496     },
16497
16498     // private
16499     onViewClick : function(view, doFocus, el, e)
16500     {
16501         var index = this.view.getSelectedIndexes()[0];
16502         
16503         var r = this.store.getAt(index);
16504         
16505         if(this.tickable){
16506             
16507             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16508                 return;
16509             }
16510             
16511             var rm = false;
16512             var _this = this;
16513             
16514             Roo.each(this.tickItems, function(v,k){
16515                 
16516                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16517                     Roo.log(v);
16518                     _this.tickItems.splice(k, 1);
16519                     
16520                     if(typeof(e) == 'undefined' && view == false){
16521                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16522                     }
16523                     
16524                     rm = true;
16525                     return;
16526                 }
16527             });
16528             
16529             if(rm){
16530                 return;
16531             }
16532             
16533             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16534                 this.tickItems.push(r.data);
16535             }
16536             
16537             if(typeof(e) == 'undefined' && view == false){
16538                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16539             }
16540                     
16541             return;
16542         }
16543         
16544         if(r){
16545             this.onSelect(r, index);
16546         }
16547         if(doFocus !== false && !this.blockFocus){
16548             this.inputEl().focus();
16549         }
16550     },
16551
16552     // private
16553     restrictHeight : function(){
16554         //this.innerList.dom.style.height = '';
16555         //var inner = this.innerList.dom;
16556         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16557         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16558         //this.list.beginUpdate();
16559         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16560         this.list.alignTo(this.inputEl(), this.listAlign);
16561         this.list.alignTo(this.inputEl(), this.listAlign);
16562         //this.list.endUpdate();
16563     },
16564
16565     // private
16566     onEmptyResults : function(){
16567         
16568         if(this.tickable && this.editable){
16569             this.hasFocus = false;
16570             this.restrictHeight();
16571             return;
16572         }
16573         
16574         this.collapse();
16575     },
16576
16577     /**
16578      * Returns true if the dropdown list is expanded, else false.
16579      */
16580     isExpanded : function(){
16581         return this.list.isVisible();
16582     },
16583
16584     /**
16585      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16586      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16587      * @param {String} value The data value of the item to select
16588      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16589      * selected item if it is not currently in view (defaults to true)
16590      * @return {Boolean} True if the value matched an item in the list, else false
16591      */
16592     selectByValue : function(v, scrollIntoView){
16593         if(v !== undefined && v !== null){
16594             var r = this.findRecord(this.valueField || this.displayField, v);
16595             if(r){
16596                 this.select(this.store.indexOf(r), scrollIntoView);
16597                 return true;
16598             }
16599         }
16600         return false;
16601     },
16602
16603     /**
16604      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16605      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16606      * @param {Number} index The zero-based index of the list item to select
16607      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16608      * selected item if it is not currently in view (defaults to true)
16609      */
16610     select : function(index, scrollIntoView){
16611         this.selectedIndex = index;
16612         this.view.select(index);
16613         if(scrollIntoView !== false){
16614             var el = this.view.getNode(index);
16615             /*
16616              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16617              */
16618             if(el){
16619                 this.list.scrollChildIntoView(el, false);
16620             }
16621         }
16622     },
16623
16624     // private
16625     selectNext : function(){
16626         var ct = this.store.getCount();
16627         if(ct > 0){
16628             if(this.selectedIndex == -1){
16629                 this.select(0);
16630             }else if(this.selectedIndex < ct-1){
16631                 this.select(this.selectedIndex+1);
16632             }
16633         }
16634     },
16635
16636     // private
16637     selectPrev : function(){
16638         var ct = this.store.getCount();
16639         if(ct > 0){
16640             if(this.selectedIndex == -1){
16641                 this.select(0);
16642             }else if(this.selectedIndex != 0){
16643                 this.select(this.selectedIndex-1);
16644             }
16645         }
16646     },
16647
16648     // private
16649     onKeyUp : function(e){
16650         if(this.editable !== false && !e.isSpecialKey()){
16651             this.lastKey = e.getKey();
16652             this.dqTask.delay(this.queryDelay);
16653         }
16654     },
16655
16656     // private
16657     validateBlur : function(){
16658         return !this.list || !this.list.isVisible();   
16659     },
16660
16661     // private
16662     initQuery : function(){
16663         
16664         var v = this.getRawValue();
16665         
16666         if(this.tickable && this.editable){
16667             v = this.tickableInputEl().getValue();
16668         }
16669         
16670         this.doQuery(v);
16671     },
16672
16673     // private
16674     doForce : function(){
16675         if(this.inputEl().dom.value.length > 0){
16676             this.inputEl().dom.value =
16677                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16678              
16679         }
16680     },
16681
16682     /**
16683      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16684      * query allowing the query action to be canceled if needed.
16685      * @param {String} query The SQL query to execute
16686      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16687      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16688      * saved in the current store (defaults to false)
16689      */
16690     doQuery : function(q, forceAll){
16691         
16692         if(q === undefined || q === null){
16693             q = '';
16694         }
16695         var qe = {
16696             query: q,
16697             forceAll: forceAll,
16698             combo: this,
16699             cancel:false
16700         };
16701         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16702             return false;
16703         }
16704         q = qe.query;
16705         
16706         forceAll = qe.forceAll;
16707         if(forceAll === true || (q.length >= this.minChars)){
16708             
16709             this.hasQuery = true;
16710             
16711             if(this.lastQuery != q || this.alwaysQuery){
16712                 this.lastQuery = q;
16713                 if(this.mode == 'local'){
16714                     this.selectedIndex = -1;
16715                     if(forceAll){
16716                         this.store.clearFilter();
16717                     }else{
16718                         
16719                         if(this.specialFilter){
16720                             this.fireEvent('specialfilter', this);
16721                             this.onLoad();
16722                             return;
16723                         }
16724                         
16725                         this.store.filter(this.displayField, q);
16726                     }
16727                     
16728                     this.store.fireEvent("datachanged", this.store);
16729                     
16730                     this.onLoad();
16731                     
16732                     
16733                 }else{
16734                     
16735                     this.store.baseParams[this.queryParam] = q;
16736                     
16737                     var options = {params : this.getParams(q)};
16738                     
16739                     if(this.loadNext){
16740                         options.add = true;
16741                         options.params.start = this.page * this.pageSize;
16742                     }
16743                     
16744                     this.store.load(options);
16745                     
16746                     /*
16747                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16748                      *  we should expand the list on onLoad
16749                      *  so command out it
16750                      */
16751 //                    this.expand();
16752                 }
16753             }else{
16754                 this.selectedIndex = -1;
16755                 this.onLoad();   
16756             }
16757         }
16758         
16759         this.loadNext = false;
16760     },
16761     
16762     // private
16763     getParams : function(q){
16764         var p = {};
16765         //p[this.queryParam] = q;
16766         
16767         if(this.pageSize){
16768             p.start = 0;
16769             p.limit = this.pageSize;
16770         }
16771         return p;
16772     },
16773
16774     /**
16775      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16776      */
16777     collapse : function(){
16778         if(!this.isExpanded()){
16779             return;
16780         }
16781         
16782         this.list.hide();
16783         
16784         this.hasFocus = false;
16785         
16786         if(this.tickable){
16787             this.okBtn.hide();
16788             this.cancelBtn.hide();
16789             this.trigger.show();
16790             
16791             if(this.editable){
16792                 this.tickableInputEl().dom.value = '';
16793                 this.tickableInputEl().blur();
16794             }
16795             
16796         }
16797         
16798         Roo.get(document).un('mousedown', this.collapseIf, this);
16799         Roo.get(document).un('mousewheel', this.collapseIf, this);
16800         if (!this.editable) {
16801             Roo.get(document).un('keydown', this.listKeyPress, this);
16802         }
16803         this.fireEvent('collapse', this);
16804         
16805         this.validate();
16806     },
16807
16808     // private
16809     collapseIf : function(e){
16810         var in_combo  = e.within(this.el);
16811         var in_list =  e.within(this.list);
16812         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16813         
16814         if (in_combo || in_list || is_list) {
16815             //e.stopPropagation();
16816             return;
16817         }
16818         
16819         if(this.tickable){
16820             this.onTickableFooterButtonClick(e, false, false);
16821         }
16822
16823         this.collapse();
16824         
16825     },
16826
16827     /**
16828      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16829      */
16830     expand : function(){
16831        
16832         if(this.isExpanded() || !this.hasFocus){
16833             return;
16834         }
16835         
16836         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16837         this.list.setWidth(lw);
16838         
16839         Roo.log('expand');
16840         
16841         this.list.show();
16842         
16843         this.restrictHeight();
16844         
16845         if(this.tickable){
16846             
16847             this.tickItems = Roo.apply([], this.item);
16848             
16849             this.okBtn.show();
16850             this.cancelBtn.show();
16851             this.trigger.hide();
16852             
16853             if(this.editable){
16854                 this.tickableInputEl().focus();
16855             }
16856             
16857         }
16858         
16859         Roo.get(document).on('mousedown', this.collapseIf, this);
16860         Roo.get(document).on('mousewheel', this.collapseIf, this);
16861         if (!this.editable) {
16862             Roo.get(document).on('keydown', this.listKeyPress, this);
16863         }
16864         
16865         this.fireEvent('expand', this);
16866     },
16867
16868     // private
16869     // Implements the default empty TriggerField.onTriggerClick function
16870     onTriggerClick : function(e)
16871     {
16872         Roo.log('trigger click');
16873         
16874         if(this.disabled || !this.triggerList){
16875             return;
16876         }
16877         
16878         this.page = 0;
16879         this.loadNext = false;
16880         
16881         if(this.isExpanded()){
16882             this.collapse();
16883             if (!this.blockFocus) {
16884                 this.inputEl().focus();
16885             }
16886             
16887         }else {
16888             this.hasFocus = true;
16889             if(this.triggerAction == 'all') {
16890                 this.doQuery(this.allQuery, true);
16891             } else {
16892                 this.doQuery(this.getRawValue());
16893             }
16894             if (!this.blockFocus) {
16895                 this.inputEl().focus();
16896             }
16897         }
16898     },
16899     
16900     onTickableTriggerClick : function(e)
16901     {
16902         if(this.disabled){
16903             return;
16904         }
16905         
16906         this.page = 0;
16907         this.loadNext = false;
16908         this.hasFocus = true;
16909         
16910         if(this.triggerAction == 'all') {
16911             this.doQuery(this.allQuery, true);
16912         } else {
16913             this.doQuery(this.getRawValue());
16914         }
16915     },
16916     
16917     onSearchFieldClick : function(e)
16918     {
16919         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16920             this.onTickableFooterButtonClick(e, false, false);
16921             return;
16922         }
16923         
16924         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16925             return;
16926         }
16927         
16928         this.page = 0;
16929         this.loadNext = false;
16930         this.hasFocus = true;
16931         
16932         if(this.triggerAction == 'all') {
16933             this.doQuery(this.allQuery, true);
16934         } else {
16935             this.doQuery(this.getRawValue());
16936         }
16937     },
16938     
16939     listKeyPress : function(e)
16940     {
16941         //Roo.log('listkeypress');
16942         // scroll to first matching element based on key pres..
16943         if (e.isSpecialKey()) {
16944             return false;
16945         }
16946         var k = String.fromCharCode(e.getKey()).toUpperCase();
16947         //Roo.log(k);
16948         var match  = false;
16949         var csel = this.view.getSelectedNodes();
16950         var cselitem = false;
16951         if (csel.length) {
16952             var ix = this.view.indexOf(csel[0]);
16953             cselitem  = this.store.getAt(ix);
16954             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16955                 cselitem = false;
16956             }
16957             
16958         }
16959         
16960         this.store.each(function(v) { 
16961             if (cselitem) {
16962                 // start at existing selection.
16963                 if (cselitem.id == v.id) {
16964                     cselitem = false;
16965                 }
16966                 return true;
16967             }
16968                 
16969             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16970                 match = this.store.indexOf(v);
16971                 return false;
16972             }
16973             return true;
16974         }, this);
16975         
16976         if (match === false) {
16977             return true; // no more action?
16978         }
16979         // scroll to?
16980         this.view.select(match);
16981         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16982         sn.scrollIntoView(sn.dom.parentNode, false);
16983     },
16984     
16985     onViewScroll : function(e, t){
16986         
16987         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){
16988             return;
16989         }
16990         
16991         this.hasQuery = true;
16992         
16993         this.loading = this.list.select('.loading', true).first();
16994         
16995         if(this.loading === null){
16996             this.list.createChild({
16997                 tag: 'div',
16998                 cls: 'loading roo-select2-more-results roo-select2-active',
16999                 html: 'Loading more results...'
17000             });
17001             
17002             this.loading = this.list.select('.loading', true).first();
17003             
17004             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17005             
17006             this.loading.hide();
17007         }
17008         
17009         this.loading.show();
17010         
17011         var _combo = this;
17012         
17013         this.page++;
17014         this.loadNext = true;
17015         
17016         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17017         
17018         return;
17019     },
17020     
17021     addItem : function(o)
17022     {   
17023         var dv = ''; // display value
17024         
17025         if (this.displayField) {
17026             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17027         } else {
17028             // this is an error condition!!!
17029             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17030         }
17031         
17032         if(!dv.length){
17033             return;
17034         }
17035         
17036         var choice = this.choices.createChild({
17037             tag: 'li',
17038             cls: 'roo-select2-search-choice',
17039             cn: [
17040                 {
17041                     tag: 'div',
17042                     html: dv
17043                 },
17044                 {
17045                     tag: 'a',
17046                     href: '#',
17047                     cls: 'roo-select2-search-choice-close fa fa-times',
17048                     tabindex: '-1'
17049                 }
17050             ]
17051             
17052         }, this.searchField);
17053         
17054         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17055         
17056         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17057         
17058         this.item.push(o);
17059         
17060         this.lastData = o;
17061         
17062         this.syncValue();
17063         
17064         this.inputEl().dom.value = '';
17065         
17066         this.validate();
17067     },
17068     
17069     onRemoveItem : function(e, _self, o)
17070     {
17071         e.preventDefault();
17072         
17073         this.lastItem = Roo.apply([], this.item);
17074         
17075         var index = this.item.indexOf(o.data) * 1;
17076         
17077         if( index < 0){
17078             Roo.log('not this item?!');
17079             return;
17080         }
17081         
17082         this.item.splice(index, 1);
17083         o.item.remove();
17084         
17085         this.syncValue();
17086         
17087         this.fireEvent('remove', this, e);
17088         
17089         this.validate();
17090         
17091     },
17092     
17093     syncValue : function()
17094     {
17095         if(!this.item.length){
17096             this.clearValue();
17097             return;
17098         }
17099             
17100         var value = [];
17101         var _this = this;
17102         Roo.each(this.item, function(i){
17103             if(_this.valueField){
17104                 value.push(i[_this.valueField]);
17105                 return;
17106             }
17107
17108             value.push(i);
17109         });
17110
17111         this.value = value.join(',');
17112
17113         if(this.hiddenField){
17114             this.hiddenField.dom.value = this.value;
17115         }
17116         
17117         this.store.fireEvent("datachanged", this.store);
17118         
17119         this.validate();
17120     },
17121     
17122     clearItem : function()
17123     {
17124         if(!this.multiple){
17125             return;
17126         }
17127         
17128         this.item = [];
17129         
17130         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17131            c.remove();
17132         });
17133         
17134         this.syncValue();
17135         
17136         this.validate();
17137         
17138         if(this.tickable && !Roo.isTouch){
17139             this.view.refresh();
17140         }
17141     },
17142     
17143     inputEl: function ()
17144     {
17145         if(Roo.isIOS && this.useNativeIOS){
17146             return this.el.select('select.roo-ios-select', true).first();
17147         }
17148         
17149         if(Roo.isTouch && this.mobileTouchView){
17150             return this.el.select('input.form-control',true).first();
17151         }
17152         
17153         if(this.tickable){
17154             return this.searchField;
17155         }
17156         
17157         return this.el.select('input.form-control',true).first();
17158     },
17159     
17160     onTickableFooterButtonClick : function(e, btn, el)
17161     {
17162         e.preventDefault();
17163         
17164         this.lastItem = Roo.apply([], this.item);
17165         
17166         if(btn && btn.name == 'cancel'){
17167             this.tickItems = Roo.apply([], this.item);
17168             this.collapse();
17169             return;
17170         }
17171         
17172         this.clearItem();
17173         
17174         var _this = this;
17175         
17176         Roo.each(this.tickItems, function(o){
17177             _this.addItem(o);
17178         });
17179         
17180         this.collapse();
17181         
17182     },
17183     
17184     validate : function()
17185     {
17186         if(this.getVisibilityEl().hasClass('hidden')){
17187             return true;
17188         }
17189         
17190         var v = this.getRawValue();
17191         
17192         if(this.multiple){
17193             v = this.getValue();
17194         }
17195         
17196         if(this.disabled || this.allowBlank || v.length){
17197             this.markValid();
17198             return true;
17199         }
17200         
17201         this.markInvalid();
17202         return false;
17203     },
17204     
17205     tickableInputEl : function()
17206     {
17207         if(!this.tickable || !this.editable){
17208             return this.inputEl();
17209         }
17210         
17211         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17212     },
17213     
17214     
17215     getAutoCreateTouchView : function()
17216     {
17217         var id = Roo.id();
17218         
17219         var cfg = {
17220             cls: 'form-group' //input-group
17221         };
17222         
17223         var input =  {
17224             tag: 'input',
17225             id : id,
17226             type : this.inputType,
17227             cls : 'form-control x-combo-noedit',
17228             autocomplete: 'new-password',
17229             placeholder : this.placeholder || '',
17230             readonly : true
17231         };
17232         
17233         if (this.name) {
17234             input.name = this.name;
17235         }
17236         
17237         if (this.size) {
17238             input.cls += ' input-' + this.size;
17239         }
17240         
17241         if (this.disabled) {
17242             input.disabled = true;
17243         }
17244         
17245         var inputblock = {
17246             cls : 'roo-combobox-wrap',
17247             cn : [
17248                 input
17249             ]
17250         };
17251         
17252         if(this.before){
17253             inputblock.cls += ' input-group';
17254             
17255             inputblock.cn.unshift({
17256                 tag :'span',
17257                 cls : 'input-group-addon input-group-prepend input-group-text',
17258                 html : this.before
17259             });
17260         }
17261         
17262         if(this.removable && !this.multiple){
17263             inputblock.cls += ' roo-removable';
17264             
17265             inputblock.cn.push({
17266                 tag: 'button',
17267                 html : 'x',
17268                 cls : 'roo-combo-removable-btn close'
17269             });
17270         }
17271
17272         if(this.hasFeedback && !this.allowBlank){
17273             
17274             inputblock.cls += ' has-feedback';
17275             
17276             inputblock.cn.push({
17277                 tag: 'span',
17278                 cls: 'glyphicon form-control-feedback'
17279             });
17280             
17281         }
17282         
17283         if (this.after) {
17284             
17285             inputblock.cls += (this.before) ? '' : ' input-group';
17286             
17287             inputblock.cn.push({
17288                 tag :'span',
17289                 cls : 'input-group-addon input-group-append input-group-text',
17290                 html : this.after
17291             });
17292         }
17293
17294         
17295         var ibwrap = inputblock;
17296         
17297         if(this.multiple){
17298             ibwrap = {
17299                 tag: 'ul',
17300                 cls: 'roo-select2-choices',
17301                 cn:[
17302                     {
17303                         tag: 'li',
17304                         cls: 'roo-select2-search-field',
17305                         cn: [
17306
17307                             inputblock
17308                         ]
17309                     }
17310                 ]
17311             };
17312         
17313             
17314         }
17315         
17316         var combobox = {
17317             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17318             cn: [
17319                 {
17320                     tag: 'input',
17321                     type : 'hidden',
17322                     cls: 'form-hidden-field'
17323                 },
17324                 ibwrap
17325             ]
17326         };
17327         
17328         if(!this.multiple && this.showToggleBtn){
17329             
17330             var caret = {
17331                 cls: 'caret'
17332             };
17333             
17334             if (this.caret != false) {
17335                 caret = {
17336                      tag: 'i',
17337                      cls: 'fa fa-' + this.caret
17338                 };
17339                 
17340             }
17341             
17342             combobox.cn.push({
17343                 tag :'span',
17344                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17345                 cn : [
17346                     Roo.bootstrap.version == 3 ? caret : '',
17347                     {
17348                         tag: 'span',
17349                         cls: 'combobox-clear',
17350                         cn  : [
17351                             {
17352                                 tag : 'i',
17353                                 cls: 'icon-remove'
17354                             }
17355                         ]
17356                     }
17357                 ]
17358
17359             })
17360         }
17361         
17362         if(this.multiple){
17363             combobox.cls += ' roo-select2-container-multi';
17364         }
17365         
17366         var align = this.labelAlign || this.parentLabelAlign();
17367         
17368         if (align ==='left' && this.fieldLabel.length) {
17369
17370             cfg.cn = [
17371                 {
17372                    tag : 'i',
17373                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17374                    tooltip : 'This field is required'
17375                 },
17376                 {
17377                     tag: 'label',
17378                     cls : 'control-label col-form-label',
17379                     html : this.fieldLabel
17380
17381                 },
17382                 {
17383                     cls : 'roo-combobox-wrap ', 
17384                     cn: [
17385                         combobox
17386                     ]
17387                 }
17388             ];
17389             
17390             var labelCfg = cfg.cn[1];
17391             var contentCfg = cfg.cn[2];
17392             
17393
17394             if(this.indicatorpos == 'right'){
17395                 cfg.cn = [
17396                     {
17397                         tag: 'label',
17398                         'for' :  id,
17399                         cls : 'control-label col-form-label',
17400                         cn : [
17401                             {
17402                                 tag : 'span',
17403                                 html : this.fieldLabel
17404                             },
17405                             {
17406                                 tag : 'i',
17407                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17408                                 tooltip : 'This field is required'
17409                             }
17410                         ]
17411                     },
17412                     {
17413                         cls : "roo-combobox-wrap ",
17414                         cn: [
17415                             combobox
17416                         ]
17417                     }
17418
17419                 ];
17420                 
17421                 labelCfg = cfg.cn[0];
17422                 contentCfg = cfg.cn[1];
17423             }
17424             
17425            
17426             
17427             if(this.labelWidth > 12){
17428                 labelCfg.style = "width: " + this.labelWidth + 'px';
17429             }
17430            
17431             if(this.labelWidth < 13 && this.labelmd == 0){
17432                 this.labelmd = this.labelWidth;
17433             }
17434             
17435             if(this.labellg > 0){
17436                 labelCfg.cls += ' col-lg-' + this.labellg;
17437                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17438             }
17439             
17440             if(this.labelmd > 0){
17441                 labelCfg.cls += ' col-md-' + this.labelmd;
17442                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17443             }
17444             
17445             if(this.labelsm > 0){
17446                 labelCfg.cls += ' col-sm-' + this.labelsm;
17447                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17448             }
17449             
17450             if(this.labelxs > 0){
17451                 labelCfg.cls += ' col-xs-' + this.labelxs;
17452                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17453             }
17454                 
17455                 
17456         } else if ( this.fieldLabel.length) {
17457             cfg.cn = [
17458                 {
17459                    tag : 'i',
17460                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17461                    tooltip : 'This field is required'
17462                 },
17463                 {
17464                     tag: 'label',
17465                     cls : 'control-label',
17466                     html : this.fieldLabel
17467
17468                 },
17469                 {
17470                     cls : '', 
17471                     cn: [
17472                         combobox
17473                     ]
17474                 }
17475             ];
17476             
17477             if(this.indicatorpos == 'right'){
17478                 cfg.cn = [
17479                     {
17480                         tag: 'label',
17481                         cls : 'control-label',
17482                         html : this.fieldLabel,
17483                         cn : [
17484                             {
17485                                tag : 'i',
17486                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17487                                tooltip : 'This field is required'
17488                             }
17489                         ]
17490                     },
17491                     {
17492                         cls : '', 
17493                         cn: [
17494                             combobox
17495                         ]
17496                     }
17497                 ];
17498             }
17499         } else {
17500             cfg.cn = combobox;    
17501         }
17502         
17503         
17504         var settings = this;
17505         
17506         ['xs','sm','md','lg'].map(function(size){
17507             if (settings[size]) {
17508                 cfg.cls += ' col-' + size + '-' + settings[size];
17509             }
17510         });
17511         
17512         return cfg;
17513     },
17514     
17515     initTouchView : function()
17516     {
17517         this.renderTouchView();
17518         
17519         this.touchViewEl.on('scroll', function(){
17520             this.el.dom.scrollTop = 0;
17521         }, this);
17522         
17523         this.originalValue = this.getValue();
17524         
17525         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17526         
17527         this.inputEl().on("click", this.showTouchView, this);
17528         if (this.triggerEl) {
17529             this.triggerEl.on("click", this.showTouchView, this);
17530         }
17531         
17532         
17533         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17534         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17535         
17536         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17537         
17538         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17539         this.store.on('load', this.onTouchViewLoad, this);
17540         this.store.on('loadexception', this.onTouchViewLoadException, this);
17541         
17542         if(this.hiddenName){
17543             
17544             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17545             
17546             this.hiddenField.dom.value =
17547                 this.hiddenValue !== undefined ? this.hiddenValue :
17548                 this.value !== undefined ? this.value : '';
17549         
17550             this.el.dom.removeAttribute('name');
17551             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17552         }
17553         
17554         if(this.multiple){
17555             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17556             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17557         }
17558         
17559         if(this.removable && !this.multiple){
17560             var close = this.closeTriggerEl();
17561             if(close){
17562                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17563                 close.on('click', this.removeBtnClick, this, close);
17564             }
17565         }
17566         /*
17567          * fix the bug in Safari iOS8
17568          */
17569         this.inputEl().on("focus", function(e){
17570             document.activeElement.blur();
17571         }, this);
17572         
17573         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17574         
17575         return;
17576         
17577         
17578     },
17579     
17580     renderTouchView : function()
17581     {
17582         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17583         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17584         
17585         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17586         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17587         
17588         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17589         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17590         this.touchViewBodyEl.setStyle('overflow', 'auto');
17591         
17592         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17593         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17594         
17595         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17596         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17597         
17598     },
17599     
17600     showTouchView : function()
17601     {
17602         if(this.disabled){
17603             return;
17604         }
17605         
17606         this.touchViewHeaderEl.hide();
17607
17608         if(this.modalTitle.length){
17609             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17610             this.touchViewHeaderEl.show();
17611         }
17612
17613         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17614         this.touchViewEl.show();
17615
17616         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17617         
17618         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17619         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17620
17621         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17622
17623         if(this.modalTitle.length){
17624             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17625         }
17626         
17627         this.touchViewBodyEl.setHeight(bodyHeight);
17628
17629         if(this.animate){
17630             var _this = this;
17631             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17632         }else{
17633             this.touchViewEl.addClass(['in','show']);
17634         }
17635         
17636         if(this._touchViewMask){
17637             Roo.get(document.body).addClass("x-body-masked");
17638             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17639             this._touchViewMask.setStyle('z-index', 10000);
17640             this._touchViewMask.addClass('show');
17641         }
17642         
17643         this.doTouchViewQuery();
17644         
17645     },
17646     
17647     hideTouchView : function()
17648     {
17649         this.touchViewEl.removeClass(['in','show']);
17650
17651         if(this.animate){
17652             var _this = this;
17653             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17654         }else{
17655             this.touchViewEl.setStyle('display', 'none');
17656         }
17657         
17658         if(this._touchViewMask){
17659             this._touchViewMask.removeClass('show');
17660             Roo.get(document.body).removeClass("x-body-masked");
17661         }
17662     },
17663     
17664     setTouchViewValue : function()
17665     {
17666         if(this.multiple){
17667             this.clearItem();
17668         
17669             var _this = this;
17670
17671             Roo.each(this.tickItems, function(o){
17672                 this.addItem(o);
17673             }, this);
17674         }
17675         
17676         this.hideTouchView();
17677     },
17678     
17679     doTouchViewQuery : function()
17680     {
17681         var qe = {
17682             query: '',
17683             forceAll: true,
17684             combo: this,
17685             cancel:false
17686         };
17687         
17688         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17689             return false;
17690         }
17691         
17692         if(!this.alwaysQuery || this.mode == 'local'){
17693             this.onTouchViewLoad();
17694             return;
17695         }
17696         
17697         this.store.load();
17698     },
17699     
17700     onTouchViewBeforeLoad : function(combo,opts)
17701     {
17702         return;
17703     },
17704
17705     // private
17706     onTouchViewLoad : function()
17707     {
17708         if(this.store.getCount() < 1){
17709             this.onTouchViewEmptyResults();
17710             return;
17711         }
17712         
17713         this.clearTouchView();
17714         
17715         var rawValue = this.getRawValue();
17716         
17717         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17718         
17719         this.tickItems = [];
17720         
17721         this.store.data.each(function(d, rowIndex){
17722             var row = this.touchViewListGroup.createChild(template);
17723             
17724             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17725                 row.addClass(d.data.cls);
17726             }
17727             
17728             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17729                 var cfg = {
17730                     data : d.data,
17731                     html : d.data[this.displayField]
17732                 };
17733                 
17734                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17735                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17736                 }
17737             }
17738             row.removeClass('selected');
17739             if(!this.multiple && this.valueField &&
17740                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17741             {
17742                 // radio buttons..
17743                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17744                 row.addClass('selected');
17745             }
17746             
17747             if(this.multiple && this.valueField &&
17748                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17749             {
17750                 
17751                 // checkboxes...
17752                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17753                 this.tickItems.push(d.data);
17754             }
17755             
17756             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17757             
17758         }, this);
17759         
17760         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17761         
17762         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17763
17764         if(this.modalTitle.length){
17765             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17766         }
17767
17768         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17769         
17770         if(this.mobile_restrict_height && listHeight < bodyHeight){
17771             this.touchViewBodyEl.setHeight(listHeight);
17772         }
17773         
17774         var _this = this;
17775         
17776         if(firstChecked && listHeight > bodyHeight){
17777             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17778         }
17779         
17780     },
17781     
17782     onTouchViewLoadException : function()
17783     {
17784         this.hideTouchView();
17785     },
17786     
17787     onTouchViewEmptyResults : function()
17788     {
17789         this.clearTouchView();
17790         
17791         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17792         
17793         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17794         
17795     },
17796     
17797     clearTouchView : function()
17798     {
17799         this.touchViewListGroup.dom.innerHTML = '';
17800     },
17801     
17802     onTouchViewClick : function(e, el, o)
17803     {
17804         e.preventDefault();
17805         
17806         var row = o.row;
17807         var rowIndex = o.rowIndex;
17808         
17809         var r = this.store.getAt(rowIndex);
17810         
17811         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17812             
17813             if(!this.multiple){
17814                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17815                     c.dom.removeAttribute('checked');
17816                 }, this);
17817
17818                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17819
17820                 this.setFromData(r.data);
17821
17822                 var close = this.closeTriggerEl();
17823
17824                 if(close){
17825                     close.show();
17826                 }
17827
17828                 this.hideTouchView();
17829
17830                 this.fireEvent('select', this, r, rowIndex);
17831
17832                 return;
17833             }
17834
17835             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17836                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17837                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17838                 return;
17839             }
17840
17841             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17842             this.addItem(r.data);
17843             this.tickItems.push(r.data);
17844         }
17845     },
17846     
17847     getAutoCreateNativeIOS : function()
17848     {
17849         var cfg = {
17850             cls: 'form-group' //input-group,
17851         };
17852         
17853         var combobox =  {
17854             tag: 'select',
17855             cls : 'roo-ios-select'
17856         };
17857         
17858         if (this.name) {
17859             combobox.name = this.name;
17860         }
17861         
17862         if (this.disabled) {
17863             combobox.disabled = true;
17864         }
17865         
17866         var settings = this;
17867         
17868         ['xs','sm','md','lg'].map(function(size){
17869             if (settings[size]) {
17870                 cfg.cls += ' col-' + size + '-' + settings[size];
17871             }
17872         });
17873         
17874         cfg.cn = combobox;
17875         
17876         return cfg;
17877         
17878     },
17879     
17880     initIOSView : function()
17881     {
17882         this.store.on('load', this.onIOSViewLoad, this);
17883         
17884         return;
17885     },
17886     
17887     onIOSViewLoad : function()
17888     {
17889         if(this.store.getCount() < 1){
17890             return;
17891         }
17892         
17893         this.clearIOSView();
17894         
17895         if(this.allowBlank) {
17896             
17897             var default_text = '-- SELECT --';
17898             
17899             if(this.placeholder.length){
17900                 default_text = this.placeholder;
17901             }
17902             
17903             if(this.emptyTitle.length){
17904                 default_text += ' - ' + this.emptyTitle + ' -';
17905             }
17906             
17907             var opt = this.inputEl().createChild({
17908                 tag: 'option',
17909                 value : 0,
17910                 html : default_text
17911             });
17912             
17913             var o = {};
17914             o[this.valueField] = 0;
17915             o[this.displayField] = default_text;
17916             
17917             this.ios_options.push({
17918                 data : o,
17919                 el : opt
17920             });
17921             
17922         }
17923         
17924         this.store.data.each(function(d, rowIndex){
17925             
17926             var html = '';
17927             
17928             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17929                 html = d.data[this.displayField];
17930             }
17931             
17932             var value = '';
17933             
17934             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17935                 value = d.data[this.valueField];
17936             }
17937             
17938             var option = {
17939                 tag: 'option',
17940                 value : value,
17941                 html : html
17942             };
17943             
17944             if(this.value == d.data[this.valueField]){
17945                 option['selected'] = true;
17946             }
17947             
17948             var opt = this.inputEl().createChild(option);
17949             
17950             this.ios_options.push({
17951                 data : d.data,
17952                 el : opt
17953             });
17954             
17955         }, this);
17956         
17957         this.inputEl().on('change', function(){
17958            this.fireEvent('select', this);
17959         }, this);
17960         
17961     },
17962     
17963     clearIOSView: function()
17964     {
17965         this.inputEl().dom.innerHTML = '';
17966         
17967         this.ios_options = [];
17968     },
17969     
17970     setIOSValue: function(v)
17971     {
17972         this.value = v;
17973         
17974         if(!this.ios_options){
17975             return;
17976         }
17977         
17978         Roo.each(this.ios_options, function(opts){
17979            
17980            opts.el.dom.removeAttribute('selected');
17981            
17982            if(opts.data[this.valueField] != v){
17983                return;
17984            }
17985            
17986            opts.el.dom.setAttribute('selected', true);
17987            
17988         }, this);
17989     }
17990
17991     /** 
17992     * @cfg {Boolean} grow 
17993     * @hide 
17994     */
17995     /** 
17996     * @cfg {Number} growMin 
17997     * @hide 
17998     */
17999     /** 
18000     * @cfg {Number} growMax 
18001     * @hide 
18002     */
18003     /**
18004      * @hide
18005      * @method autoSize
18006      */
18007 });
18008
18009 Roo.apply(Roo.bootstrap.ComboBox,  {
18010     
18011     header : {
18012         tag: 'div',
18013         cls: 'modal-header',
18014         cn: [
18015             {
18016                 tag: 'h4',
18017                 cls: 'modal-title'
18018             }
18019         ]
18020     },
18021     
18022     body : {
18023         tag: 'div',
18024         cls: 'modal-body',
18025         cn: [
18026             {
18027                 tag: 'ul',
18028                 cls: 'list-group'
18029             }
18030         ]
18031     },
18032     
18033     listItemRadio : {
18034         tag: 'li',
18035         cls: 'list-group-item',
18036         cn: [
18037             {
18038                 tag: 'span',
18039                 cls: 'roo-combobox-list-group-item-value'
18040             },
18041             {
18042                 tag: 'div',
18043                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18044                 cn: [
18045                     {
18046                         tag: 'input',
18047                         type: 'radio'
18048                     },
18049                     {
18050                         tag: 'label'
18051                     }
18052                 ]
18053             }
18054         ]
18055     },
18056     
18057     listItemCheckbox : {
18058         tag: 'li',
18059         cls: 'list-group-item',
18060         cn: [
18061             {
18062                 tag: 'span',
18063                 cls: 'roo-combobox-list-group-item-value'
18064             },
18065             {
18066                 tag: 'div',
18067                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18068                 cn: [
18069                     {
18070                         tag: 'input',
18071                         type: 'checkbox'
18072                     },
18073                     {
18074                         tag: 'label'
18075                     }
18076                 ]
18077             }
18078         ]
18079     },
18080     
18081     emptyResult : {
18082         tag: 'div',
18083         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18084     },
18085     
18086     footer : {
18087         tag: 'div',
18088         cls: 'modal-footer',
18089         cn: [
18090             {
18091                 tag: 'div',
18092                 cls: 'row',
18093                 cn: [
18094                     {
18095                         tag: 'div',
18096                         cls: 'col-xs-6 text-left',
18097                         cn: {
18098                             tag: 'button',
18099                             cls: 'btn btn-danger roo-touch-view-cancel',
18100                             html: 'Cancel'
18101                         }
18102                     },
18103                     {
18104                         tag: 'div',
18105                         cls: 'col-xs-6 text-right',
18106                         cn: {
18107                             tag: 'button',
18108                             cls: 'btn btn-success roo-touch-view-ok',
18109                             html: 'OK'
18110                         }
18111                     }
18112                 ]
18113             }
18114         ]
18115         
18116     }
18117 });
18118
18119 Roo.apply(Roo.bootstrap.ComboBox,  {
18120     
18121     touchViewTemplate : {
18122         tag: 'div',
18123         cls: 'modal fade roo-combobox-touch-view',
18124         cn: [
18125             {
18126                 tag: 'div',
18127                 cls: 'modal-dialog',
18128                 style : 'position:fixed', // we have to fix position....
18129                 cn: [
18130                     {
18131                         tag: 'div',
18132                         cls: 'modal-content',
18133                         cn: [
18134                             Roo.bootstrap.ComboBox.header,
18135                             Roo.bootstrap.ComboBox.body,
18136                             Roo.bootstrap.ComboBox.footer
18137                         ]
18138                     }
18139                 ]
18140             }
18141         ]
18142     }
18143 });/*
18144  * Based on:
18145  * Ext JS Library 1.1.1
18146  * Copyright(c) 2006-2007, Ext JS, LLC.
18147  *
18148  * Originally Released Under LGPL - original licence link has changed is not relivant.
18149  *
18150  * Fork - LGPL
18151  * <script type="text/javascript">
18152  */
18153
18154 /**
18155  * @class Roo.View
18156  * @extends Roo.util.Observable
18157  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18158  * This class also supports single and multi selection modes. <br>
18159  * Create a data model bound view:
18160  <pre><code>
18161  var store = new Roo.data.Store(...);
18162
18163  var view = new Roo.View({
18164     el : "my-element",
18165     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18166  
18167     singleSelect: true,
18168     selectedClass: "ydataview-selected",
18169     store: store
18170  });
18171
18172  // listen for node click?
18173  view.on("click", function(vw, index, node, e){
18174  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18175  });
18176
18177  // load XML data
18178  dataModel.load("foobar.xml");
18179  </code></pre>
18180  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18181  * <br><br>
18182  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18183  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18184  * 
18185  * Note: old style constructor is still suported (container, template, config)
18186  * 
18187  * @constructor
18188  * Create a new View
18189  * @param {Object} config The config object
18190  * 
18191  */
18192 Roo.View = function(config, depreciated_tpl, depreciated_config){
18193     
18194     this.parent = false;
18195     
18196     if (typeof(depreciated_tpl) == 'undefined') {
18197         // new way.. - universal constructor.
18198         Roo.apply(this, config);
18199         this.el  = Roo.get(this.el);
18200     } else {
18201         // old format..
18202         this.el  = Roo.get(config);
18203         this.tpl = depreciated_tpl;
18204         Roo.apply(this, depreciated_config);
18205     }
18206     this.wrapEl  = this.el.wrap().wrap();
18207     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18208     
18209     
18210     if(typeof(this.tpl) == "string"){
18211         this.tpl = new Roo.Template(this.tpl);
18212     } else {
18213         // support xtype ctors..
18214         this.tpl = new Roo.factory(this.tpl, Roo);
18215     }
18216     
18217     
18218     this.tpl.compile();
18219     
18220     /** @private */
18221     this.addEvents({
18222         /**
18223          * @event beforeclick
18224          * Fires before a click is processed. Returns false to cancel the default action.
18225          * @param {Roo.View} this
18226          * @param {Number} index The index of the target node
18227          * @param {HTMLElement} node The target node
18228          * @param {Roo.EventObject} e The raw event object
18229          */
18230             "beforeclick" : true,
18231         /**
18232          * @event click
18233          * Fires when a template node is clicked.
18234          * @param {Roo.View} this
18235          * @param {Number} index The index of the target node
18236          * @param {HTMLElement} node The target node
18237          * @param {Roo.EventObject} e The raw event object
18238          */
18239             "click" : true,
18240         /**
18241          * @event dblclick
18242          * Fires when a template node is double clicked.
18243          * @param {Roo.View} this
18244          * @param {Number} index The index of the target node
18245          * @param {HTMLElement} node The target node
18246          * @param {Roo.EventObject} e The raw event object
18247          */
18248             "dblclick" : true,
18249         /**
18250          * @event contextmenu
18251          * Fires when a template node is right clicked.
18252          * @param {Roo.View} this
18253          * @param {Number} index The index of the target node
18254          * @param {HTMLElement} node The target node
18255          * @param {Roo.EventObject} e The raw event object
18256          */
18257             "contextmenu" : true,
18258         /**
18259          * @event selectionchange
18260          * Fires when the selected nodes change.
18261          * @param {Roo.View} this
18262          * @param {Array} selections Array of the selected nodes
18263          */
18264             "selectionchange" : true,
18265     
18266         /**
18267          * @event beforeselect
18268          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18269          * @param {Roo.View} this
18270          * @param {HTMLElement} node The node to be selected
18271          * @param {Array} selections Array of currently selected nodes
18272          */
18273             "beforeselect" : true,
18274         /**
18275          * @event preparedata
18276          * Fires on every row to render, to allow you to change the data.
18277          * @param {Roo.View} this
18278          * @param {Object} data to be rendered (change this)
18279          */
18280           "preparedata" : true
18281           
18282           
18283         });
18284
18285
18286
18287     this.el.on({
18288         "click": this.onClick,
18289         "dblclick": this.onDblClick,
18290         "contextmenu": this.onContextMenu,
18291         scope:this
18292     });
18293
18294     this.selections = [];
18295     this.nodes = [];
18296     this.cmp = new Roo.CompositeElementLite([]);
18297     if(this.store){
18298         this.store = Roo.factory(this.store, Roo.data);
18299         this.setStore(this.store, true);
18300     }
18301     
18302     if ( this.footer && this.footer.xtype) {
18303            
18304          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18305         
18306         this.footer.dataSource = this.store;
18307         this.footer.container = fctr;
18308         this.footer = Roo.factory(this.footer, Roo);
18309         fctr.insertFirst(this.el);
18310         
18311         // this is a bit insane - as the paging toolbar seems to detach the el..
18312 //        dom.parentNode.parentNode.parentNode
18313          // they get detached?
18314     }
18315     
18316     
18317     Roo.View.superclass.constructor.call(this);
18318     
18319     
18320 };
18321
18322 Roo.extend(Roo.View, Roo.util.Observable, {
18323     
18324      /**
18325      * @cfg {Roo.data.Store} store Data store to load data from.
18326      */
18327     store : false,
18328     
18329     /**
18330      * @cfg {String|Roo.Element} el The container element.
18331      */
18332     el : '',
18333     
18334     /**
18335      * @cfg {String|Roo.Template} tpl The template used by this View 
18336      */
18337     tpl : false,
18338     /**
18339      * @cfg {String} dataName the named area of the template to use as the data area
18340      *                          Works with domtemplates roo-name="name"
18341      */
18342     dataName: false,
18343     /**
18344      * @cfg {String} selectedClass The css class to add to selected nodes
18345      */
18346     selectedClass : "x-view-selected",
18347      /**
18348      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18349      */
18350     emptyText : "",
18351     
18352     /**
18353      * @cfg {String} text to display on mask (default Loading)
18354      */
18355     mask : false,
18356     /**
18357      * @cfg {Boolean} multiSelect Allow multiple selection
18358      */
18359     multiSelect : false,
18360     /**
18361      * @cfg {Boolean} singleSelect Allow single selection
18362      */
18363     singleSelect:  false,
18364     
18365     /**
18366      * @cfg {Boolean} toggleSelect - selecting 
18367      */
18368     toggleSelect : false,
18369     
18370     /**
18371      * @cfg {Boolean} tickable - selecting 
18372      */
18373     tickable : false,
18374     
18375     /**
18376      * Returns the element this view is bound to.
18377      * @return {Roo.Element}
18378      */
18379     getEl : function(){
18380         return this.wrapEl;
18381     },
18382     
18383     
18384
18385     /**
18386      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18387      */
18388     refresh : function(){
18389         //Roo.log('refresh');
18390         var t = this.tpl;
18391         
18392         // if we are using something like 'domtemplate', then
18393         // the what gets used is:
18394         // t.applySubtemplate(NAME, data, wrapping data..)
18395         // the outer template then get' applied with
18396         //     the store 'extra data'
18397         // and the body get's added to the
18398         //      roo-name="data" node?
18399         //      <span class='roo-tpl-{name}'></span> ?????
18400         
18401         
18402         
18403         this.clearSelections();
18404         this.el.update("");
18405         var html = [];
18406         var records = this.store.getRange();
18407         if(records.length < 1) {
18408             
18409             // is this valid??  = should it render a template??
18410             
18411             this.el.update(this.emptyText);
18412             return;
18413         }
18414         var el = this.el;
18415         if (this.dataName) {
18416             this.el.update(t.apply(this.store.meta)); //????
18417             el = this.el.child('.roo-tpl-' + this.dataName);
18418         }
18419         
18420         for(var i = 0, len = records.length; i < len; i++){
18421             var data = this.prepareData(records[i].data, i, records[i]);
18422             this.fireEvent("preparedata", this, data, i, records[i]);
18423             
18424             var d = Roo.apply({}, data);
18425             
18426             if(this.tickable){
18427                 Roo.apply(d, {'roo-id' : Roo.id()});
18428                 
18429                 var _this = this;
18430             
18431                 Roo.each(this.parent.item, function(item){
18432                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18433                         return;
18434                     }
18435                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18436                 });
18437             }
18438             
18439             html[html.length] = Roo.util.Format.trim(
18440                 this.dataName ?
18441                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18442                     t.apply(d)
18443             );
18444         }
18445         
18446         
18447         
18448         el.update(html.join(""));
18449         this.nodes = el.dom.childNodes;
18450         this.updateIndexes(0);
18451     },
18452     
18453
18454     /**
18455      * Function to override to reformat the data that is sent to
18456      * the template for each node.
18457      * DEPRICATED - use the preparedata event handler.
18458      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18459      * a JSON object for an UpdateManager bound view).
18460      */
18461     prepareData : function(data, index, record)
18462     {
18463         this.fireEvent("preparedata", this, data, index, record);
18464         return data;
18465     },
18466
18467     onUpdate : function(ds, record){
18468         // Roo.log('on update');   
18469         this.clearSelections();
18470         var index = this.store.indexOf(record);
18471         var n = this.nodes[index];
18472         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18473         n.parentNode.removeChild(n);
18474         this.updateIndexes(index, index);
18475     },
18476
18477     
18478     
18479 // --------- FIXME     
18480     onAdd : function(ds, records, index)
18481     {
18482         //Roo.log(['on Add', ds, records, index] );        
18483         this.clearSelections();
18484         if(this.nodes.length == 0){
18485             this.refresh();
18486             return;
18487         }
18488         var n = this.nodes[index];
18489         for(var i = 0, len = records.length; i < len; i++){
18490             var d = this.prepareData(records[i].data, i, records[i]);
18491             if(n){
18492                 this.tpl.insertBefore(n, d);
18493             }else{
18494                 
18495                 this.tpl.append(this.el, d);
18496             }
18497         }
18498         this.updateIndexes(index);
18499     },
18500
18501     onRemove : function(ds, record, index){
18502        // Roo.log('onRemove');
18503         this.clearSelections();
18504         var el = this.dataName  ?
18505             this.el.child('.roo-tpl-' + this.dataName) :
18506             this.el; 
18507         
18508         el.dom.removeChild(this.nodes[index]);
18509         this.updateIndexes(index);
18510     },
18511
18512     /**
18513      * Refresh an individual node.
18514      * @param {Number} index
18515      */
18516     refreshNode : function(index){
18517         this.onUpdate(this.store, this.store.getAt(index));
18518     },
18519
18520     updateIndexes : function(startIndex, endIndex){
18521         var ns = this.nodes;
18522         startIndex = startIndex || 0;
18523         endIndex = endIndex || ns.length - 1;
18524         for(var i = startIndex; i <= endIndex; i++){
18525             ns[i].nodeIndex = i;
18526         }
18527     },
18528
18529     /**
18530      * Changes the data store this view uses and refresh the view.
18531      * @param {Store} store
18532      */
18533     setStore : function(store, initial){
18534         if(!initial && this.store){
18535             this.store.un("datachanged", this.refresh);
18536             this.store.un("add", this.onAdd);
18537             this.store.un("remove", this.onRemove);
18538             this.store.un("update", this.onUpdate);
18539             this.store.un("clear", this.refresh);
18540             this.store.un("beforeload", this.onBeforeLoad);
18541             this.store.un("load", this.onLoad);
18542             this.store.un("loadexception", this.onLoad);
18543         }
18544         if(store){
18545           
18546             store.on("datachanged", this.refresh, this);
18547             store.on("add", this.onAdd, this);
18548             store.on("remove", this.onRemove, this);
18549             store.on("update", this.onUpdate, this);
18550             store.on("clear", this.refresh, this);
18551             store.on("beforeload", this.onBeforeLoad, this);
18552             store.on("load", this.onLoad, this);
18553             store.on("loadexception", this.onLoad, this);
18554         }
18555         
18556         if(store){
18557             this.refresh();
18558         }
18559     },
18560     /**
18561      * onbeforeLoad - masks the loading area.
18562      *
18563      */
18564     onBeforeLoad : function(store,opts)
18565     {
18566          //Roo.log('onBeforeLoad');   
18567         if (!opts.add) {
18568             this.el.update("");
18569         }
18570         this.el.mask(this.mask ? this.mask : "Loading" ); 
18571     },
18572     onLoad : function ()
18573     {
18574         this.el.unmask();
18575     },
18576     
18577
18578     /**
18579      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18580      * @param {HTMLElement} node
18581      * @return {HTMLElement} The template node
18582      */
18583     findItemFromChild : function(node){
18584         var el = this.dataName  ?
18585             this.el.child('.roo-tpl-' + this.dataName,true) :
18586             this.el.dom; 
18587         
18588         if(!node || node.parentNode == el){
18589                     return node;
18590             }
18591             var p = node.parentNode;
18592             while(p && p != el){
18593             if(p.parentNode == el){
18594                 return p;
18595             }
18596             p = p.parentNode;
18597         }
18598             return null;
18599     },
18600
18601     /** @ignore */
18602     onClick : function(e){
18603         var item = this.findItemFromChild(e.getTarget());
18604         if(item){
18605             var index = this.indexOf(item);
18606             if(this.onItemClick(item, index, e) !== false){
18607                 this.fireEvent("click", this, index, item, e);
18608             }
18609         }else{
18610             this.clearSelections();
18611         }
18612     },
18613
18614     /** @ignore */
18615     onContextMenu : function(e){
18616         var item = this.findItemFromChild(e.getTarget());
18617         if(item){
18618             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18619         }
18620     },
18621
18622     /** @ignore */
18623     onDblClick : function(e){
18624         var item = this.findItemFromChild(e.getTarget());
18625         if(item){
18626             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18627         }
18628     },
18629
18630     onItemClick : function(item, index, e)
18631     {
18632         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18633             return false;
18634         }
18635         if (this.toggleSelect) {
18636             var m = this.isSelected(item) ? 'unselect' : 'select';
18637             //Roo.log(m);
18638             var _t = this;
18639             _t[m](item, true, false);
18640             return true;
18641         }
18642         if(this.multiSelect || this.singleSelect){
18643             if(this.multiSelect && e.shiftKey && this.lastSelection){
18644                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18645             }else{
18646                 this.select(item, this.multiSelect && e.ctrlKey);
18647                 this.lastSelection = item;
18648             }
18649             
18650             if(!this.tickable){
18651                 e.preventDefault();
18652             }
18653             
18654         }
18655         return true;
18656     },
18657
18658     /**
18659      * Get the number of selected nodes.
18660      * @return {Number}
18661      */
18662     getSelectionCount : function(){
18663         return this.selections.length;
18664     },
18665
18666     /**
18667      * Get the currently selected nodes.
18668      * @return {Array} An array of HTMLElements
18669      */
18670     getSelectedNodes : function(){
18671         return this.selections;
18672     },
18673
18674     /**
18675      * Get the indexes of the selected nodes.
18676      * @return {Array}
18677      */
18678     getSelectedIndexes : function(){
18679         var indexes = [], s = this.selections;
18680         for(var i = 0, len = s.length; i < len; i++){
18681             indexes.push(s[i].nodeIndex);
18682         }
18683         return indexes;
18684     },
18685
18686     /**
18687      * Clear all selections
18688      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18689      */
18690     clearSelections : function(suppressEvent){
18691         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18692             this.cmp.elements = this.selections;
18693             this.cmp.removeClass(this.selectedClass);
18694             this.selections = [];
18695             if(!suppressEvent){
18696                 this.fireEvent("selectionchange", this, this.selections);
18697             }
18698         }
18699     },
18700
18701     /**
18702      * Returns true if the passed node is selected
18703      * @param {HTMLElement/Number} node The node or node index
18704      * @return {Boolean}
18705      */
18706     isSelected : function(node){
18707         var s = this.selections;
18708         if(s.length < 1){
18709             return false;
18710         }
18711         node = this.getNode(node);
18712         return s.indexOf(node) !== -1;
18713     },
18714
18715     /**
18716      * Selects nodes.
18717      * @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
18718      * @param {Boolean} keepExisting (optional) true to keep existing selections
18719      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18720      */
18721     select : function(nodeInfo, keepExisting, suppressEvent){
18722         if(nodeInfo instanceof Array){
18723             if(!keepExisting){
18724                 this.clearSelections(true);
18725             }
18726             for(var i = 0, len = nodeInfo.length; i < len; i++){
18727                 this.select(nodeInfo[i], true, true);
18728             }
18729             return;
18730         } 
18731         var node = this.getNode(nodeInfo);
18732         if(!node || this.isSelected(node)){
18733             return; // already selected.
18734         }
18735         if(!keepExisting){
18736             this.clearSelections(true);
18737         }
18738         
18739         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18740             Roo.fly(node).addClass(this.selectedClass);
18741             this.selections.push(node);
18742             if(!suppressEvent){
18743                 this.fireEvent("selectionchange", this, this.selections);
18744             }
18745         }
18746         
18747         
18748     },
18749       /**
18750      * Unselects nodes.
18751      * @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
18752      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18753      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18754      */
18755     unselect : function(nodeInfo, keepExisting, suppressEvent)
18756     {
18757         if(nodeInfo instanceof Array){
18758             Roo.each(this.selections, function(s) {
18759                 this.unselect(s, nodeInfo);
18760             }, this);
18761             return;
18762         }
18763         var node = this.getNode(nodeInfo);
18764         if(!node || !this.isSelected(node)){
18765             //Roo.log("not selected");
18766             return; // not selected.
18767         }
18768         // fireevent???
18769         var ns = [];
18770         Roo.each(this.selections, function(s) {
18771             if (s == node ) {
18772                 Roo.fly(node).removeClass(this.selectedClass);
18773
18774                 return;
18775             }
18776             ns.push(s);
18777         },this);
18778         
18779         this.selections= ns;
18780         this.fireEvent("selectionchange", this, this.selections);
18781     },
18782
18783     /**
18784      * Gets a template node.
18785      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18786      * @return {HTMLElement} The node or null if it wasn't found
18787      */
18788     getNode : function(nodeInfo){
18789         if(typeof nodeInfo == "string"){
18790             return document.getElementById(nodeInfo);
18791         }else if(typeof nodeInfo == "number"){
18792             return this.nodes[nodeInfo];
18793         }
18794         return nodeInfo;
18795     },
18796
18797     /**
18798      * Gets a range template nodes.
18799      * @param {Number} startIndex
18800      * @param {Number} endIndex
18801      * @return {Array} An array of nodes
18802      */
18803     getNodes : function(start, end){
18804         var ns = this.nodes;
18805         start = start || 0;
18806         end = typeof end == "undefined" ? ns.length - 1 : end;
18807         var nodes = [];
18808         if(start <= end){
18809             for(var i = start; i <= end; i++){
18810                 nodes.push(ns[i]);
18811             }
18812         } else{
18813             for(var i = start; i >= end; i--){
18814                 nodes.push(ns[i]);
18815             }
18816         }
18817         return nodes;
18818     },
18819
18820     /**
18821      * Finds the index of the passed node
18822      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18823      * @return {Number} The index of the node or -1
18824      */
18825     indexOf : function(node){
18826         node = this.getNode(node);
18827         if(typeof node.nodeIndex == "number"){
18828             return node.nodeIndex;
18829         }
18830         var ns = this.nodes;
18831         for(var i = 0, len = ns.length; i < len; i++){
18832             if(ns[i] == node){
18833                 return i;
18834             }
18835         }
18836         return -1;
18837     }
18838 });
18839 /*
18840  * - LGPL
18841  *
18842  * based on jquery fullcalendar
18843  * 
18844  */
18845
18846 Roo.bootstrap = Roo.bootstrap || {};
18847 /**
18848  * @class Roo.bootstrap.Calendar
18849  * @extends Roo.bootstrap.Component
18850  * Bootstrap Calendar class
18851  * @cfg {Boolean} loadMask (true|false) default false
18852  * @cfg {Object} header generate the user specific header of the calendar, default false
18853
18854  * @constructor
18855  * Create a new Container
18856  * @param {Object} config The config object
18857  */
18858
18859
18860
18861 Roo.bootstrap.Calendar = function(config){
18862     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18863      this.addEvents({
18864         /**
18865              * @event select
18866              * Fires when a date is selected
18867              * @param {DatePicker} this
18868              * @param {Date} date The selected date
18869              */
18870         'select': true,
18871         /**
18872              * @event monthchange
18873              * Fires when the displayed month changes 
18874              * @param {DatePicker} this
18875              * @param {Date} date The selected month
18876              */
18877         'monthchange': true,
18878         /**
18879              * @event evententer
18880              * Fires when mouse over an event
18881              * @param {Calendar} this
18882              * @param {event} Event
18883              */
18884         'evententer': true,
18885         /**
18886              * @event eventleave
18887              * Fires when the mouse leaves an
18888              * @param {Calendar} this
18889              * @param {event}
18890              */
18891         'eventleave': true,
18892         /**
18893              * @event eventclick
18894              * Fires when the mouse click an
18895              * @param {Calendar} this
18896              * @param {event}
18897              */
18898         'eventclick': true
18899         
18900     });
18901
18902 };
18903
18904 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18905     
18906      /**
18907      * @cfg {Number} startDay
18908      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18909      */
18910     startDay : 0,
18911     
18912     loadMask : false,
18913     
18914     header : false,
18915       
18916     getAutoCreate : function(){
18917         
18918         
18919         var fc_button = function(name, corner, style, content ) {
18920             return Roo.apply({},{
18921                 tag : 'span',
18922                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18923                          (corner.length ?
18924                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18925                             ''
18926                         ),
18927                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18928                 unselectable: 'on'
18929             });
18930         };
18931         
18932         var header = {};
18933         
18934         if(!this.header){
18935             header = {
18936                 tag : 'table',
18937                 cls : 'fc-header',
18938                 style : 'width:100%',
18939                 cn : [
18940                     {
18941                         tag: 'tr',
18942                         cn : [
18943                             {
18944                                 tag : 'td',
18945                                 cls : 'fc-header-left',
18946                                 cn : [
18947                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18948                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18949                                     { tag: 'span', cls: 'fc-header-space' },
18950                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18951
18952
18953                                 ]
18954                             },
18955
18956                             {
18957                                 tag : 'td',
18958                                 cls : 'fc-header-center',
18959                                 cn : [
18960                                     {
18961                                         tag: 'span',
18962                                         cls: 'fc-header-title',
18963                                         cn : {
18964                                             tag: 'H2',
18965                                             html : 'month / year'
18966                                         }
18967                                     }
18968
18969                                 ]
18970                             },
18971                             {
18972                                 tag : 'td',
18973                                 cls : 'fc-header-right',
18974                                 cn : [
18975                               /*      fc_button('month', 'left', '', 'month' ),
18976                                     fc_button('week', '', '', 'week' ),
18977                                     fc_button('day', 'right', '', 'day' )
18978                                 */    
18979
18980                                 ]
18981                             }
18982
18983                         ]
18984                     }
18985                 ]
18986             };
18987         }
18988         
18989         header = this.header;
18990         
18991        
18992         var cal_heads = function() {
18993             var ret = [];
18994             // fixme - handle this.
18995             
18996             for (var i =0; i < Date.dayNames.length; i++) {
18997                 var d = Date.dayNames[i];
18998                 ret.push({
18999                     tag: 'th',
19000                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19001                     html : d.substring(0,3)
19002                 });
19003                 
19004             }
19005             ret[0].cls += ' fc-first';
19006             ret[6].cls += ' fc-last';
19007             return ret;
19008         };
19009         var cal_cell = function(n) {
19010             return  {
19011                 tag: 'td',
19012                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19013                 cn : [
19014                     {
19015                         cn : [
19016                             {
19017                                 cls: 'fc-day-number',
19018                                 html: 'D'
19019                             },
19020                             {
19021                                 cls: 'fc-day-content',
19022                              
19023                                 cn : [
19024                                      {
19025                                         style: 'position: relative;' // height: 17px;
19026                                     }
19027                                 ]
19028                             }
19029                             
19030                             
19031                         ]
19032                     }
19033                 ]
19034                 
19035             }
19036         };
19037         var cal_rows = function() {
19038             
19039             var ret = [];
19040             for (var r = 0; r < 6; r++) {
19041                 var row= {
19042                     tag : 'tr',
19043                     cls : 'fc-week',
19044                     cn : []
19045                 };
19046                 
19047                 for (var i =0; i < Date.dayNames.length; i++) {
19048                     var d = Date.dayNames[i];
19049                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19050
19051                 }
19052                 row.cn[0].cls+=' fc-first';
19053                 row.cn[0].cn[0].style = 'min-height:90px';
19054                 row.cn[6].cls+=' fc-last';
19055                 ret.push(row);
19056                 
19057             }
19058             ret[0].cls += ' fc-first';
19059             ret[4].cls += ' fc-prev-last';
19060             ret[5].cls += ' fc-last';
19061             return ret;
19062             
19063         };
19064         
19065         var cal_table = {
19066             tag: 'table',
19067             cls: 'fc-border-separate',
19068             style : 'width:100%',
19069             cellspacing  : 0,
19070             cn : [
19071                 { 
19072                     tag: 'thead',
19073                     cn : [
19074                         { 
19075                             tag: 'tr',
19076                             cls : 'fc-first fc-last',
19077                             cn : cal_heads()
19078                         }
19079                     ]
19080                 },
19081                 { 
19082                     tag: 'tbody',
19083                     cn : cal_rows()
19084                 }
19085                   
19086             ]
19087         };
19088          
19089          var cfg = {
19090             cls : 'fc fc-ltr',
19091             cn : [
19092                 header,
19093                 {
19094                     cls : 'fc-content',
19095                     style : "position: relative;",
19096                     cn : [
19097                         {
19098                             cls : 'fc-view fc-view-month fc-grid',
19099                             style : 'position: relative',
19100                             unselectable : 'on',
19101                             cn : [
19102                                 {
19103                                     cls : 'fc-event-container',
19104                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19105                                 },
19106                                 cal_table
19107                             ]
19108                         }
19109                     ]
19110     
19111                 }
19112            ] 
19113             
19114         };
19115         
19116          
19117         
19118         return cfg;
19119     },
19120     
19121     
19122     initEvents : function()
19123     {
19124         if(!this.store){
19125             throw "can not find store for calendar";
19126         }
19127         
19128         var mark = {
19129             tag: "div",
19130             cls:"x-dlg-mask",
19131             style: "text-align:center",
19132             cn: [
19133                 {
19134                     tag: "div",
19135                     style: "background-color:white;width:50%;margin:250 auto",
19136                     cn: [
19137                         {
19138                             tag: "img",
19139                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19140                         },
19141                         {
19142                             tag: "span",
19143                             html: "Loading"
19144                         }
19145                         
19146                     ]
19147                 }
19148             ]
19149         };
19150         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19151         
19152         var size = this.el.select('.fc-content', true).first().getSize();
19153         this.maskEl.setSize(size.width, size.height);
19154         this.maskEl.enableDisplayMode("block");
19155         if(!this.loadMask){
19156             this.maskEl.hide();
19157         }
19158         
19159         this.store = Roo.factory(this.store, Roo.data);
19160         this.store.on('load', this.onLoad, this);
19161         this.store.on('beforeload', this.onBeforeLoad, this);
19162         
19163         this.resize();
19164         
19165         this.cells = this.el.select('.fc-day',true);
19166         //Roo.log(this.cells);
19167         this.textNodes = this.el.query('.fc-day-number');
19168         this.cells.addClassOnOver('fc-state-hover');
19169         
19170         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19171         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19172         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19173         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19174         
19175         this.on('monthchange', this.onMonthChange, this);
19176         
19177         this.update(new Date().clearTime());
19178     },
19179     
19180     resize : function() {
19181         var sz  = this.el.getSize();
19182         
19183         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19184         this.el.select('.fc-day-content div',true).setHeight(34);
19185     },
19186     
19187     
19188     // private
19189     showPrevMonth : function(e){
19190         this.update(this.activeDate.add("mo", -1));
19191     },
19192     showToday : function(e){
19193         this.update(new Date().clearTime());
19194     },
19195     // private
19196     showNextMonth : function(e){
19197         this.update(this.activeDate.add("mo", 1));
19198     },
19199
19200     // private
19201     showPrevYear : function(){
19202         this.update(this.activeDate.add("y", -1));
19203     },
19204
19205     // private
19206     showNextYear : function(){
19207         this.update(this.activeDate.add("y", 1));
19208     },
19209
19210     
19211    // private
19212     update : function(date)
19213     {
19214         var vd = this.activeDate;
19215         this.activeDate = date;
19216 //        if(vd && this.el){
19217 //            var t = date.getTime();
19218 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19219 //                Roo.log('using add remove');
19220 //                
19221 //                this.fireEvent('monthchange', this, date);
19222 //                
19223 //                this.cells.removeClass("fc-state-highlight");
19224 //                this.cells.each(function(c){
19225 //                   if(c.dateValue == t){
19226 //                       c.addClass("fc-state-highlight");
19227 //                       setTimeout(function(){
19228 //                            try{c.dom.firstChild.focus();}catch(e){}
19229 //                       }, 50);
19230 //                       return false;
19231 //                   }
19232 //                   return true;
19233 //                });
19234 //                return;
19235 //            }
19236 //        }
19237         
19238         var days = date.getDaysInMonth();
19239         
19240         var firstOfMonth = date.getFirstDateOfMonth();
19241         var startingPos = firstOfMonth.getDay()-this.startDay;
19242         
19243         if(startingPos < this.startDay){
19244             startingPos += 7;
19245         }
19246         
19247         var pm = date.add(Date.MONTH, -1);
19248         var prevStart = pm.getDaysInMonth()-startingPos;
19249 //        
19250         this.cells = this.el.select('.fc-day',true);
19251         this.textNodes = this.el.query('.fc-day-number');
19252         this.cells.addClassOnOver('fc-state-hover');
19253         
19254         var cells = this.cells.elements;
19255         var textEls = this.textNodes;
19256         
19257         Roo.each(cells, function(cell){
19258             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19259         });
19260         
19261         days += startingPos;
19262
19263         // convert everything to numbers so it's fast
19264         var day = 86400000;
19265         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19266         //Roo.log(d);
19267         //Roo.log(pm);
19268         //Roo.log(prevStart);
19269         
19270         var today = new Date().clearTime().getTime();
19271         var sel = date.clearTime().getTime();
19272         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19273         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19274         var ddMatch = this.disabledDatesRE;
19275         var ddText = this.disabledDatesText;
19276         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19277         var ddaysText = this.disabledDaysText;
19278         var format = this.format;
19279         
19280         var setCellClass = function(cal, cell){
19281             cell.row = 0;
19282             cell.events = [];
19283             cell.more = [];
19284             //Roo.log('set Cell Class');
19285             cell.title = "";
19286             var t = d.getTime();
19287             
19288             //Roo.log(d);
19289             
19290             cell.dateValue = t;
19291             if(t == today){
19292                 cell.className += " fc-today";
19293                 cell.className += " fc-state-highlight";
19294                 cell.title = cal.todayText;
19295             }
19296             if(t == sel){
19297                 // disable highlight in other month..
19298                 //cell.className += " fc-state-highlight";
19299                 
19300             }
19301             // disabling
19302             if(t < min) {
19303                 cell.className = " fc-state-disabled";
19304                 cell.title = cal.minText;
19305                 return;
19306             }
19307             if(t > max) {
19308                 cell.className = " fc-state-disabled";
19309                 cell.title = cal.maxText;
19310                 return;
19311             }
19312             if(ddays){
19313                 if(ddays.indexOf(d.getDay()) != -1){
19314                     cell.title = ddaysText;
19315                     cell.className = " fc-state-disabled";
19316                 }
19317             }
19318             if(ddMatch && format){
19319                 var fvalue = d.dateFormat(format);
19320                 if(ddMatch.test(fvalue)){
19321                     cell.title = ddText.replace("%0", fvalue);
19322                     cell.className = " fc-state-disabled";
19323                 }
19324             }
19325             
19326             if (!cell.initialClassName) {
19327                 cell.initialClassName = cell.dom.className;
19328             }
19329             
19330             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19331         };
19332
19333         var i = 0;
19334         
19335         for(; i < startingPos; i++) {
19336             textEls[i].innerHTML = (++prevStart);
19337             d.setDate(d.getDate()+1);
19338             
19339             cells[i].className = "fc-past fc-other-month";
19340             setCellClass(this, cells[i]);
19341         }
19342         
19343         var intDay = 0;
19344         
19345         for(; i < days; i++){
19346             intDay = i - startingPos + 1;
19347             textEls[i].innerHTML = (intDay);
19348             d.setDate(d.getDate()+1);
19349             
19350             cells[i].className = ''; // "x-date-active";
19351             setCellClass(this, cells[i]);
19352         }
19353         var extraDays = 0;
19354         
19355         for(; i < 42; i++) {
19356             textEls[i].innerHTML = (++extraDays);
19357             d.setDate(d.getDate()+1);
19358             
19359             cells[i].className = "fc-future fc-other-month";
19360             setCellClass(this, cells[i]);
19361         }
19362         
19363         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19364         
19365         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19366         
19367         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19368         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19369         
19370         if(totalRows != 6){
19371             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19372             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19373         }
19374         
19375         this.fireEvent('monthchange', this, date);
19376         
19377         
19378         /*
19379         if(!this.internalRender){
19380             var main = this.el.dom.firstChild;
19381             var w = main.offsetWidth;
19382             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19383             Roo.fly(main).setWidth(w);
19384             this.internalRender = true;
19385             // opera does not respect the auto grow header center column
19386             // then, after it gets a width opera refuses to recalculate
19387             // without a second pass
19388             if(Roo.isOpera && !this.secondPass){
19389                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19390                 this.secondPass = true;
19391                 this.update.defer(10, this, [date]);
19392             }
19393         }
19394         */
19395         
19396     },
19397     
19398     findCell : function(dt) {
19399         dt = dt.clearTime().getTime();
19400         var ret = false;
19401         this.cells.each(function(c){
19402             //Roo.log("check " +c.dateValue + '?=' + dt);
19403             if(c.dateValue == dt){
19404                 ret = c;
19405                 return false;
19406             }
19407             return true;
19408         });
19409         
19410         return ret;
19411     },
19412     
19413     findCells : function(ev) {
19414         var s = ev.start.clone().clearTime().getTime();
19415        // Roo.log(s);
19416         var e= ev.end.clone().clearTime().getTime();
19417        // Roo.log(e);
19418         var ret = [];
19419         this.cells.each(function(c){
19420              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19421             
19422             if(c.dateValue > e){
19423                 return ;
19424             }
19425             if(c.dateValue < s){
19426                 return ;
19427             }
19428             ret.push(c);
19429         });
19430         
19431         return ret;    
19432     },
19433     
19434 //    findBestRow: function(cells)
19435 //    {
19436 //        var ret = 0;
19437 //        
19438 //        for (var i =0 ; i < cells.length;i++) {
19439 //            ret  = Math.max(cells[i].rows || 0,ret);
19440 //        }
19441 //        return ret;
19442 //        
19443 //    },
19444     
19445     
19446     addItem : function(ev)
19447     {
19448         // look for vertical location slot in
19449         var cells = this.findCells(ev);
19450         
19451 //        ev.row = this.findBestRow(cells);
19452         
19453         // work out the location.
19454         
19455         var crow = false;
19456         var rows = [];
19457         for(var i =0; i < cells.length; i++) {
19458             
19459             cells[i].row = cells[0].row;
19460             
19461             if(i == 0){
19462                 cells[i].row = cells[i].row + 1;
19463             }
19464             
19465             if (!crow) {
19466                 crow = {
19467                     start : cells[i],
19468                     end :  cells[i]
19469                 };
19470                 continue;
19471             }
19472             if (crow.start.getY() == cells[i].getY()) {
19473                 // on same row.
19474                 crow.end = cells[i];
19475                 continue;
19476             }
19477             // different row.
19478             rows.push(crow);
19479             crow = {
19480                 start: cells[i],
19481                 end : cells[i]
19482             };
19483             
19484         }
19485         
19486         rows.push(crow);
19487         ev.els = [];
19488         ev.rows = rows;
19489         ev.cells = cells;
19490         
19491         cells[0].events.push(ev);
19492         
19493         this.calevents.push(ev);
19494     },
19495     
19496     clearEvents: function() {
19497         
19498         if(!this.calevents){
19499             return;
19500         }
19501         
19502         Roo.each(this.cells.elements, function(c){
19503             c.row = 0;
19504             c.events = [];
19505             c.more = [];
19506         });
19507         
19508         Roo.each(this.calevents, function(e) {
19509             Roo.each(e.els, function(el) {
19510                 el.un('mouseenter' ,this.onEventEnter, this);
19511                 el.un('mouseleave' ,this.onEventLeave, this);
19512                 el.remove();
19513             },this);
19514         },this);
19515         
19516         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19517             e.remove();
19518         });
19519         
19520     },
19521     
19522     renderEvents: function()
19523     {   
19524         var _this = this;
19525         
19526         this.cells.each(function(c) {
19527             
19528             if(c.row < 5){
19529                 return;
19530             }
19531             
19532             var ev = c.events;
19533             
19534             var r = 4;
19535             if(c.row != c.events.length){
19536                 r = 4 - (4 - (c.row - c.events.length));
19537             }
19538             
19539             c.events = ev.slice(0, r);
19540             c.more = ev.slice(r);
19541             
19542             if(c.more.length && c.more.length == 1){
19543                 c.events.push(c.more.pop());
19544             }
19545             
19546             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19547             
19548         });
19549             
19550         this.cells.each(function(c) {
19551             
19552             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19553             
19554             
19555             for (var e = 0; e < c.events.length; e++){
19556                 var ev = c.events[e];
19557                 var rows = ev.rows;
19558                 
19559                 for(var i = 0; i < rows.length; i++) {
19560                 
19561                     // how many rows should it span..
19562
19563                     var  cfg = {
19564                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19565                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19566
19567                         unselectable : "on",
19568                         cn : [
19569                             {
19570                                 cls: 'fc-event-inner',
19571                                 cn : [
19572     //                                {
19573     //                                  tag:'span',
19574     //                                  cls: 'fc-event-time',
19575     //                                  html : cells.length > 1 ? '' : ev.time
19576     //                                },
19577                                     {
19578                                       tag:'span',
19579                                       cls: 'fc-event-title',
19580                                       html : String.format('{0}', ev.title)
19581                                     }
19582
19583
19584                                 ]
19585                             },
19586                             {
19587                                 cls: 'ui-resizable-handle ui-resizable-e',
19588                                 html : '&nbsp;&nbsp;&nbsp'
19589                             }
19590
19591                         ]
19592                     };
19593
19594                     if (i == 0) {
19595                         cfg.cls += ' fc-event-start';
19596                     }
19597                     if ((i+1) == rows.length) {
19598                         cfg.cls += ' fc-event-end';
19599                     }
19600
19601                     var ctr = _this.el.select('.fc-event-container',true).first();
19602                     var cg = ctr.createChild(cfg);
19603
19604                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19605                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19606
19607                     var r = (c.more.length) ? 1 : 0;
19608                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19609                     cg.setWidth(ebox.right - sbox.x -2);
19610
19611                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19612                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19613                     cg.on('click', _this.onEventClick, _this, ev);
19614
19615                     ev.els.push(cg);
19616                     
19617                 }
19618                 
19619             }
19620             
19621             
19622             if(c.more.length){
19623                 var  cfg = {
19624                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19625                     style : 'position: absolute',
19626                     unselectable : "on",
19627                     cn : [
19628                         {
19629                             cls: 'fc-event-inner',
19630                             cn : [
19631                                 {
19632                                   tag:'span',
19633                                   cls: 'fc-event-title',
19634                                   html : 'More'
19635                                 }
19636
19637
19638                             ]
19639                         },
19640                         {
19641                             cls: 'ui-resizable-handle ui-resizable-e',
19642                             html : '&nbsp;&nbsp;&nbsp'
19643                         }
19644
19645                     ]
19646                 };
19647
19648                 var ctr = _this.el.select('.fc-event-container',true).first();
19649                 var cg = ctr.createChild(cfg);
19650
19651                 var sbox = c.select('.fc-day-content',true).first().getBox();
19652                 var ebox = c.select('.fc-day-content',true).first().getBox();
19653                 //Roo.log(cg);
19654                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19655                 cg.setWidth(ebox.right - sbox.x -2);
19656
19657                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19658                 
19659             }
19660             
19661         });
19662         
19663         
19664         
19665     },
19666     
19667     onEventEnter: function (e, el,event,d) {
19668         this.fireEvent('evententer', this, el, event);
19669     },
19670     
19671     onEventLeave: function (e, el,event,d) {
19672         this.fireEvent('eventleave', this, el, event);
19673     },
19674     
19675     onEventClick: function (e, el,event,d) {
19676         this.fireEvent('eventclick', this, el, event);
19677     },
19678     
19679     onMonthChange: function () {
19680         this.store.load();
19681     },
19682     
19683     onMoreEventClick: function(e, el, more)
19684     {
19685         var _this = this;
19686         
19687         this.calpopover.placement = 'right';
19688         this.calpopover.setTitle('More');
19689         
19690         this.calpopover.setContent('');
19691         
19692         var ctr = this.calpopover.el.select('.popover-content', true).first();
19693         
19694         Roo.each(more, function(m){
19695             var cfg = {
19696                 cls : 'fc-event-hori fc-event-draggable',
19697                 html : m.title
19698             };
19699             var cg = ctr.createChild(cfg);
19700             
19701             cg.on('click', _this.onEventClick, _this, m);
19702         });
19703         
19704         this.calpopover.show(el);
19705         
19706         
19707     },
19708     
19709     onLoad: function () 
19710     {   
19711         this.calevents = [];
19712         var cal = this;
19713         
19714         if(this.store.getCount() > 0){
19715             this.store.data.each(function(d){
19716                cal.addItem({
19717                     id : d.data.id,
19718                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19719                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19720                     time : d.data.start_time,
19721                     title : d.data.title,
19722                     description : d.data.description,
19723                     venue : d.data.venue
19724                 });
19725             });
19726         }
19727         
19728         this.renderEvents();
19729         
19730         if(this.calevents.length && this.loadMask){
19731             this.maskEl.hide();
19732         }
19733     },
19734     
19735     onBeforeLoad: function()
19736     {
19737         this.clearEvents();
19738         if(this.loadMask){
19739             this.maskEl.show();
19740         }
19741     }
19742 });
19743
19744  
19745  /*
19746  * - LGPL
19747  *
19748  * element
19749  * 
19750  */
19751
19752 /**
19753  * @class Roo.bootstrap.Popover
19754  * @extends Roo.bootstrap.Component
19755  * Bootstrap Popover class
19756  * @cfg {String} html contents of the popover   (or false to use children..)
19757  * @cfg {String} title of popover (or false to hide)
19758  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19759  * @cfg {String} trigger click || hover (or false to trigger manually)
19760  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19761  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19762  *      - if false and it has a 'parent' then it will be automatically added to that element
19763  *      - if string - Roo.get  will be called 
19764  * @cfg {Number} delay - delay before showing
19765  
19766  * @constructor
19767  * Create a new Popover
19768  * @param {Object} config The config object
19769  */
19770
19771 Roo.bootstrap.Popover = function(config){
19772     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19773     
19774     this.addEvents({
19775         // raw events
19776          /**
19777          * @event show
19778          * After the popover show
19779          * 
19780          * @param {Roo.bootstrap.Popover} this
19781          */
19782         "show" : true,
19783         /**
19784          * @event hide
19785          * After the popover hide
19786          * 
19787          * @param {Roo.bootstrap.Popover} this
19788          */
19789         "hide" : true
19790     });
19791 };
19792
19793 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19794     
19795     title: false,
19796     html: false,
19797     
19798     placement : 'right',
19799     trigger : 'hover', // hover
19800     modal : false,
19801     delay : 0,
19802     
19803     over: false,
19804     
19805     can_build_overlaid : false,
19806     
19807     maskEl : false, // the mask element
19808     headerEl : false,
19809     contentEl : false,
19810     alignEl : false, // when show is called with an element - this get's stored.
19811     
19812     getChildContainer : function()
19813     {
19814         return this.contentEl;
19815         
19816     },
19817     getPopoverHeader : function()
19818     {
19819         this.title = true; // flag not to hide it..
19820         this.headerEl.addClass('p-0');
19821         return this.headerEl
19822     },
19823     
19824     
19825     getAutoCreate : function(){
19826          
19827         var cfg = {
19828            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19829            style: 'display:block',
19830            cn : [
19831                 {
19832                     cls : 'arrow'
19833                 },
19834                 {
19835                     cls : 'popover-inner ',
19836                     cn : [
19837                         {
19838                             tag: 'h3',
19839                             cls: 'popover-title popover-header',
19840                             html : this.title === false ? '' : this.title
19841                         },
19842                         {
19843                             cls : 'popover-content popover-body '  + (this.cls || ''),
19844                             html : this.html || ''
19845                         }
19846                     ]
19847                     
19848                 }
19849            ]
19850         };
19851         
19852         return cfg;
19853     },
19854     /**
19855      * @param {string} the title
19856      */
19857     setTitle: function(str)
19858     {
19859         this.title = str;
19860         if (this.el) {
19861             this.headerEl.dom.innerHTML = str;
19862         }
19863         
19864     },
19865     /**
19866      * @param {string} the body content
19867      */
19868     setContent: function(str)
19869     {
19870         this.html = str;
19871         if (this.contentEl) {
19872             this.contentEl.dom.innerHTML = str;
19873         }
19874         
19875     },
19876     // as it get's added to the bottom of the page.
19877     onRender : function(ct, position)
19878     {
19879         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19880         
19881         
19882         
19883         if(!this.el){
19884             var cfg = Roo.apply({},  this.getAutoCreate());
19885             cfg.id = Roo.id();
19886             
19887             if (this.cls) {
19888                 cfg.cls += ' ' + this.cls;
19889             }
19890             if (this.style) {
19891                 cfg.style = this.style;
19892             }
19893             //Roo.log("adding to ");
19894             this.el = Roo.get(document.body).createChild(cfg, position);
19895 //            Roo.log(this.el);
19896         }
19897         
19898         this.contentEl = this.el.select('.popover-content',true).first();
19899         this.headerEl =  this.el.select('.popover-title',true).first();
19900         
19901         var nitems = [];
19902         if(typeof(this.items) != 'undefined'){
19903             var items = this.items;
19904             delete this.items;
19905
19906             for(var i =0;i < items.length;i++) {
19907                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19908             }
19909         }
19910
19911         this.items = nitems;
19912         
19913         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19914         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19915         
19916         
19917         
19918         this.initEvents();
19919     },
19920     
19921     resizeMask : function()
19922     {
19923         this.maskEl.setSize(
19924             Roo.lib.Dom.getViewWidth(true),
19925             Roo.lib.Dom.getViewHeight(true)
19926         );
19927     },
19928     
19929     initEvents : function()
19930     {
19931         
19932         if (!this.modal) { 
19933             Roo.bootstrap.Popover.register(this);
19934         }
19935          
19936         this.arrowEl = this.el.select('.arrow',true).first();
19937         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19938         this.el.enableDisplayMode('block');
19939         this.el.hide();
19940  
19941         
19942         if (this.over === false && !this.parent()) {
19943             return; 
19944         }
19945         if (this.triggers === false) {
19946             return;
19947         }
19948          
19949         // support parent
19950         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19951         var triggers = this.trigger ? this.trigger.split(' ') : [];
19952         Roo.each(triggers, function(trigger) {
19953         
19954             if (trigger == 'click') {
19955                 on_el.on('click', this.toggle, this);
19956             } else if (trigger != 'manual') {
19957                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19958                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19959       
19960                 on_el.on(eventIn  ,this.enter, this);
19961                 on_el.on(eventOut, this.leave, this);
19962             }
19963         }, this);
19964     },
19965     
19966     
19967     // private
19968     timeout : null,
19969     hoverState : null,
19970     
19971     toggle : function () {
19972         this.hoverState == 'in' ? this.leave() : this.enter();
19973     },
19974     
19975     enter : function () {
19976         
19977         clearTimeout(this.timeout);
19978     
19979         this.hoverState = 'in';
19980     
19981         if (!this.delay || !this.delay.show) {
19982             this.show();
19983             return;
19984         }
19985         var _t = this;
19986         this.timeout = setTimeout(function () {
19987             if (_t.hoverState == 'in') {
19988                 _t.show();
19989             }
19990         }, this.delay.show)
19991     },
19992     
19993     leave : function() {
19994         clearTimeout(this.timeout);
19995     
19996         this.hoverState = 'out';
19997     
19998         if (!this.delay || !this.delay.hide) {
19999             this.hide();
20000             return;
20001         }
20002         var _t = this;
20003         this.timeout = setTimeout(function () {
20004             if (_t.hoverState == 'out') {
20005                 _t.hide();
20006             }
20007         }, this.delay.hide)
20008     },
20009     /**
20010      * Show the popover
20011      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20012      * @param {string} (left|right|top|bottom) position
20013      */
20014     show : function (on_el, placement)
20015     {
20016         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20017         on_el = on_el || false; // default to false
20018          
20019         if (!on_el) {
20020             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20021                 on_el = this.parent().el;
20022             } else if (this.over) {
20023                 Roo.get(this.over);
20024             }
20025             
20026         }
20027         
20028         this.alignEl = Roo.get( on_el );
20029
20030         if (!this.el) {
20031             this.render(document.body);
20032         }
20033         
20034         
20035          
20036         
20037         if (this.title === false) {
20038             this.headerEl.hide();
20039         }
20040         
20041        
20042         this.el.show();
20043         this.el.dom.style.display = 'block';
20044          
20045  
20046         if (this.alignEl) {
20047             this.updatePosition(this.placement, true);
20048              
20049         } else {
20050             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20051             var es = this.el.getSize();
20052             var x = Roo.lib.Dom.getViewWidth()/2;
20053             var y = Roo.lib.Dom.getViewHeight()/2;
20054             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20055             
20056         }
20057
20058         
20059         //var arrow = this.el.select('.arrow',true).first();
20060         //arrow.set(align[2], 
20061         
20062         this.el.addClass('in');
20063         
20064          
20065         
20066         this.hoverState = 'in';
20067         
20068         if (this.modal) {
20069             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20070             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20071             this.maskEl.dom.style.display = 'block';
20072             this.maskEl.addClass('show');
20073         }
20074         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20075  
20076         this.fireEvent('show', this);
20077         
20078     },
20079     /**
20080      * fire this manually after loading a grid in the table for example
20081      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20082      * @param {Boolean} try and move it if we cant get right position.
20083      */
20084     updatePosition : function(placement, try_move)
20085     {
20086         // allow for calling with no parameters
20087         placement = placement   ? placement :  this.placement;
20088         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20089         
20090         this.el.removeClass([
20091             'fade','top','bottom', 'left', 'right','in',
20092             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20093         ]);
20094         this.el.addClass(placement + ' bs-popover-' + placement);
20095         
20096         if (!this.alignEl ) {
20097             return false;
20098         }
20099         
20100         switch (placement) {
20101             case 'right':
20102                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20103                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20104                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20105                     //normal display... or moved up/down.
20106                     this.el.setXY(offset);
20107                     var xy = this.alignEl.getAnchorXY('tr', false);
20108                     xy[0]+=2;xy[1]+=5;
20109                     this.arrowEl.setXY(xy);
20110                     return true;
20111                 }
20112                 // continue through...
20113                 return this.updatePosition('left', false);
20114                 
20115             
20116             case 'left':
20117                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20118                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20119                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20120                     //normal display... or moved up/down.
20121                     this.el.setXY(offset);
20122                     var xy = this.alignEl.getAnchorXY('tl', false);
20123                     xy[0]-=10;xy[1]+=5; // << fix me
20124                     this.arrowEl.setXY(xy);
20125                     return true;
20126                 }
20127                 // call self...
20128                 return this.updatePosition('right', false);
20129             
20130             case 'top':
20131                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20132                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20133                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20134                     //normal display... or moved up/down.
20135                     this.el.setXY(offset);
20136                     var xy = this.alignEl.getAnchorXY('t', false);
20137                     xy[1]-=10; // << fix me
20138                     this.arrowEl.setXY(xy);
20139                     return true;
20140                 }
20141                 // fall through
20142                return this.updatePosition('bottom', false);
20143             
20144             case 'bottom':
20145                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20146                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20147                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20148                     //normal display... or moved up/down.
20149                     this.el.setXY(offset);
20150                     var xy = this.alignEl.getAnchorXY('b', false);
20151                      xy[1]+=2; // << fix me
20152                     this.arrowEl.setXY(xy);
20153                     return true;
20154                 }
20155                 // fall through
20156                 return this.updatePosition('top', false);
20157                 
20158             
20159         }
20160         
20161         
20162         return false;
20163     },
20164     
20165     hide : function()
20166     {
20167         this.el.setXY([0,0]);
20168         this.el.removeClass('in');
20169         this.el.hide();
20170         this.hoverState = null;
20171         this.maskEl.hide(); // always..
20172         this.fireEvent('hide', this);
20173     }
20174     
20175 });
20176
20177
20178 Roo.apply(Roo.bootstrap.Popover, {
20179
20180     alignment : {
20181         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20182         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20183         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20184         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20185     },
20186     
20187     zIndex : 20001,
20188
20189     clickHander : false,
20190     
20191
20192     onMouseDown : function(e)
20193     {
20194         if (!e.getTarget(".roo-popover")) {
20195             this.hideAll();
20196         }
20197          
20198     },
20199     
20200     popups : [],
20201     
20202     register : function(popup)
20203     {
20204         if (!Roo.bootstrap.Popover.clickHandler) {
20205             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20206         }
20207         // hide other popups.
20208         this.hideAll();
20209         this.popups.push(popup);
20210     },
20211     hideAll : function()
20212     {
20213         this.popups.forEach(function(p) {
20214             p.hide();
20215         });
20216     }
20217
20218 });/*
20219  * - LGPL
20220  *
20221  * Card header - holder for the card header elements.
20222  * 
20223  */
20224
20225 /**
20226  * @class Roo.bootstrap.PopoverNav
20227  * @extends Roo.bootstrap.NavGroup
20228  * Bootstrap Popover header navigation class
20229  * @constructor
20230  * Create a new Popover Header Navigation 
20231  * @param {Object} config The config object
20232  */
20233
20234 Roo.bootstrap.PopoverNav = function(config){
20235     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20236 };
20237
20238 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20239     
20240     
20241     container_method : 'getPopoverHeader' 
20242     
20243      
20244     
20245     
20246    
20247 });
20248
20249  
20250
20251  /*
20252  * - LGPL
20253  *
20254  * Progress
20255  * 
20256  */
20257
20258 /**
20259  * @class Roo.bootstrap.Progress
20260  * @extends Roo.bootstrap.Component
20261  * Bootstrap Progress class
20262  * @cfg {Boolean} striped striped of the progress bar
20263  * @cfg {Boolean} active animated of the progress bar
20264  * 
20265  * 
20266  * @constructor
20267  * Create a new Progress
20268  * @param {Object} config The config object
20269  */
20270
20271 Roo.bootstrap.Progress = function(config){
20272     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20273 };
20274
20275 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20276     
20277     striped : false,
20278     active: false,
20279     
20280     getAutoCreate : function(){
20281         var cfg = {
20282             tag: 'div',
20283             cls: 'progress'
20284         };
20285         
20286         
20287         if(this.striped){
20288             cfg.cls += ' progress-striped';
20289         }
20290       
20291         if(this.active){
20292             cfg.cls += ' active';
20293         }
20294         
20295         
20296         return cfg;
20297     }
20298    
20299 });
20300
20301  
20302
20303  /*
20304  * - LGPL
20305  *
20306  * ProgressBar
20307  * 
20308  */
20309
20310 /**
20311  * @class Roo.bootstrap.ProgressBar
20312  * @extends Roo.bootstrap.Component
20313  * Bootstrap ProgressBar class
20314  * @cfg {Number} aria_valuenow aria-value now
20315  * @cfg {Number} aria_valuemin aria-value min
20316  * @cfg {Number} aria_valuemax aria-value max
20317  * @cfg {String} label label for the progress bar
20318  * @cfg {String} panel (success | info | warning | danger )
20319  * @cfg {String} role role of the progress bar
20320  * @cfg {String} sr_only text
20321  * 
20322  * 
20323  * @constructor
20324  * Create a new ProgressBar
20325  * @param {Object} config The config object
20326  */
20327
20328 Roo.bootstrap.ProgressBar = function(config){
20329     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20330 };
20331
20332 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20333     
20334     aria_valuenow : 0,
20335     aria_valuemin : 0,
20336     aria_valuemax : 100,
20337     label : false,
20338     panel : false,
20339     role : false,
20340     sr_only: false,
20341     
20342     getAutoCreate : function()
20343     {
20344         
20345         var cfg = {
20346             tag: 'div',
20347             cls: 'progress-bar',
20348             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20349         };
20350         
20351         if(this.sr_only){
20352             cfg.cn = {
20353                 tag: 'span',
20354                 cls: 'sr-only',
20355                 html: this.sr_only
20356             }
20357         }
20358         
20359         if(this.role){
20360             cfg.role = this.role;
20361         }
20362         
20363         if(this.aria_valuenow){
20364             cfg['aria-valuenow'] = this.aria_valuenow;
20365         }
20366         
20367         if(this.aria_valuemin){
20368             cfg['aria-valuemin'] = this.aria_valuemin;
20369         }
20370         
20371         if(this.aria_valuemax){
20372             cfg['aria-valuemax'] = this.aria_valuemax;
20373         }
20374         
20375         if(this.label && !this.sr_only){
20376             cfg.html = this.label;
20377         }
20378         
20379         if(this.panel){
20380             cfg.cls += ' progress-bar-' + this.panel;
20381         }
20382         
20383         return cfg;
20384     },
20385     
20386     update : function(aria_valuenow)
20387     {
20388         this.aria_valuenow = aria_valuenow;
20389         
20390         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20391     }
20392    
20393 });
20394
20395  
20396
20397  /*
20398  * - LGPL
20399  *
20400  * column
20401  * 
20402  */
20403
20404 /**
20405  * @class Roo.bootstrap.TabGroup
20406  * @extends Roo.bootstrap.Column
20407  * Bootstrap Column class
20408  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20409  * @cfg {Boolean} carousel true to make the group behave like a carousel
20410  * @cfg {Boolean} bullets show bullets for the panels
20411  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20412  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20413  * @cfg {Boolean} showarrow (true|false) show arrow default true
20414  * 
20415  * @constructor
20416  * Create a new TabGroup
20417  * @param {Object} config The config object
20418  */
20419
20420 Roo.bootstrap.TabGroup = function(config){
20421     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20422     if (!this.navId) {
20423         this.navId = Roo.id();
20424     }
20425     this.tabs = [];
20426     Roo.bootstrap.TabGroup.register(this);
20427     
20428 };
20429
20430 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20431     
20432     carousel : false,
20433     transition : false,
20434     bullets : 0,
20435     timer : 0,
20436     autoslide : false,
20437     slideFn : false,
20438     slideOnTouch : false,
20439     showarrow : true,
20440     
20441     getAutoCreate : function()
20442     {
20443         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20444         
20445         cfg.cls += ' tab-content';
20446         
20447         if (this.carousel) {
20448             cfg.cls += ' carousel slide';
20449             
20450             cfg.cn = [{
20451                cls : 'carousel-inner',
20452                cn : []
20453             }];
20454         
20455             if(this.bullets  && !Roo.isTouch){
20456                 
20457                 var bullets = {
20458                     cls : 'carousel-bullets',
20459                     cn : []
20460                 };
20461                
20462                 if(this.bullets_cls){
20463                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20464                 }
20465                 
20466                 bullets.cn.push({
20467                     cls : 'clear'
20468                 });
20469                 
20470                 cfg.cn[0].cn.push(bullets);
20471             }
20472             
20473             if(this.showarrow){
20474                 cfg.cn[0].cn.push({
20475                     tag : 'div',
20476                     class : 'carousel-arrow',
20477                     cn : [
20478                         {
20479                             tag : 'div',
20480                             class : 'carousel-prev',
20481                             cn : [
20482                                 {
20483                                     tag : 'i',
20484                                     class : 'fa fa-chevron-left'
20485                                 }
20486                             ]
20487                         },
20488                         {
20489                             tag : 'div',
20490                             class : 'carousel-next',
20491                             cn : [
20492                                 {
20493                                     tag : 'i',
20494                                     class : 'fa fa-chevron-right'
20495                                 }
20496                             ]
20497                         }
20498                     ]
20499                 });
20500             }
20501             
20502         }
20503         
20504         return cfg;
20505     },
20506     
20507     initEvents:  function()
20508     {
20509 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20510 //            this.el.on("touchstart", this.onTouchStart, this);
20511 //        }
20512         
20513         if(this.autoslide){
20514             var _this = this;
20515             
20516             this.slideFn = window.setInterval(function() {
20517                 _this.showPanelNext();
20518             }, this.timer);
20519         }
20520         
20521         if(this.showarrow){
20522             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20523             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20524         }
20525         
20526         
20527     },
20528     
20529 //    onTouchStart : function(e, el, o)
20530 //    {
20531 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20532 //            return;
20533 //        }
20534 //        
20535 //        this.showPanelNext();
20536 //    },
20537     
20538     
20539     getChildContainer : function()
20540     {
20541         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20542     },
20543     
20544     /**
20545     * register a Navigation item
20546     * @param {Roo.bootstrap.NavItem} the navitem to add
20547     */
20548     register : function(item)
20549     {
20550         this.tabs.push( item);
20551         item.navId = this.navId; // not really needed..
20552         this.addBullet();
20553     
20554     },
20555     
20556     getActivePanel : function()
20557     {
20558         var r = false;
20559         Roo.each(this.tabs, function(t) {
20560             if (t.active) {
20561                 r = t;
20562                 return false;
20563             }
20564             return null;
20565         });
20566         return r;
20567         
20568     },
20569     getPanelByName : function(n)
20570     {
20571         var r = false;
20572         Roo.each(this.tabs, function(t) {
20573             if (t.tabId == n) {
20574                 r = t;
20575                 return false;
20576             }
20577             return null;
20578         });
20579         return r;
20580     },
20581     indexOfPanel : function(p)
20582     {
20583         var r = false;
20584         Roo.each(this.tabs, function(t,i) {
20585             if (t.tabId == p.tabId) {
20586                 r = i;
20587                 return false;
20588             }
20589             return null;
20590         });
20591         return r;
20592     },
20593     /**
20594      * show a specific panel
20595      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20596      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20597      */
20598     showPanel : function (pan)
20599     {
20600         if(this.transition || typeof(pan) == 'undefined'){
20601             Roo.log("waiting for the transitionend");
20602             return false;
20603         }
20604         
20605         if (typeof(pan) == 'number') {
20606             pan = this.tabs[pan];
20607         }
20608         
20609         if (typeof(pan) == 'string') {
20610             pan = this.getPanelByName(pan);
20611         }
20612         
20613         var cur = this.getActivePanel();
20614         
20615         if(!pan || !cur){
20616             Roo.log('pan or acitve pan is undefined');
20617             return false;
20618         }
20619         
20620         if (pan.tabId == this.getActivePanel().tabId) {
20621             return true;
20622         }
20623         
20624         if (false === cur.fireEvent('beforedeactivate')) {
20625             return false;
20626         }
20627         
20628         if(this.bullets > 0 && !Roo.isTouch){
20629             this.setActiveBullet(this.indexOfPanel(pan));
20630         }
20631         
20632         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20633             
20634             //class="carousel-item carousel-item-next carousel-item-left"
20635             
20636             this.transition = true;
20637             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20638             var lr = dir == 'next' ? 'left' : 'right';
20639             pan.el.addClass(dir); // or prev
20640             pan.el.addClass('carousel-item-' + dir); // or prev
20641             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20642             cur.el.addClass(lr); // or right
20643             pan.el.addClass(lr);
20644             cur.el.addClass('carousel-item-' +lr); // or right
20645             pan.el.addClass('carousel-item-' +lr);
20646             
20647             
20648             var _this = this;
20649             cur.el.on('transitionend', function() {
20650                 Roo.log("trans end?");
20651                 
20652                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20653                 pan.setActive(true);
20654                 
20655                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20656                 cur.setActive(false);
20657                 
20658                 _this.transition = false;
20659                 
20660             }, this, { single:  true } );
20661             
20662             return true;
20663         }
20664         
20665         cur.setActive(false);
20666         pan.setActive(true);
20667         
20668         return true;
20669         
20670     },
20671     showPanelNext : function()
20672     {
20673         var i = this.indexOfPanel(this.getActivePanel());
20674         
20675         if (i >= this.tabs.length - 1 && !this.autoslide) {
20676             return;
20677         }
20678         
20679         if (i >= this.tabs.length - 1 && this.autoslide) {
20680             i = -1;
20681         }
20682         
20683         this.showPanel(this.tabs[i+1]);
20684     },
20685     
20686     showPanelPrev : function()
20687     {
20688         var i = this.indexOfPanel(this.getActivePanel());
20689         
20690         if (i  < 1 && !this.autoslide) {
20691             return;
20692         }
20693         
20694         if (i < 1 && this.autoslide) {
20695             i = this.tabs.length;
20696         }
20697         
20698         this.showPanel(this.tabs[i-1]);
20699     },
20700     
20701     
20702     addBullet: function()
20703     {
20704         if(!this.bullets || Roo.isTouch){
20705             return;
20706         }
20707         var ctr = this.el.select('.carousel-bullets',true).first();
20708         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20709         var bullet = ctr.createChild({
20710             cls : 'bullet bullet-' + i
20711         },ctr.dom.lastChild);
20712         
20713         
20714         var _this = this;
20715         
20716         bullet.on('click', (function(e, el, o, ii, t){
20717
20718             e.preventDefault();
20719
20720             this.showPanel(ii);
20721
20722             if(this.autoslide && this.slideFn){
20723                 clearInterval(this.slideFn);
20724                 this.slideFn = window.setInterval(function() {
20725                     _this.showPanelNext();
20726                 }, this.timer);
20727             }
20728
20729         }).createDelegate(this, [i, bullet], true));
20730                 
20731         
20732     },
20733      
20734     setActiveBullet : function(i)
20735     {
20736         if(Roo.isTouch){
20737             return;
20738         }
20739         
20740         Roo.each(this.el.select('.bullet', true).elements, function(el){
20741             el.removeClass('selected');
20742         });
20743
20744         var bullet = this.el.select('.bullet-' + i, true).first();
20745         
20746         if(!bullet){
20747             return;
20748         }
20749         
20750         bullet.addClass('selected');
20751     }
20752     
20753     
20754   
20755 });
20756
20757  
20758
20759  
20760  
20761 Roo.apply(Roo.bootstrap.TabGroup, {
20762     
20763     groups: {},
20764      /**
20765     * register a Navigation Group
20766     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20767     */
20768     register : function(navgrp)
20769     {
20770         this.groups[navgrp.navId] = navgrp;
20771         
20772     },
20773     /**
20774     * fetch a Navigation Group based on the navigation ID
20775     * if one does not exist , it will get created.
20776     * @param {string} the navgroup to add
20777     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20778     */
20779     get: function(navId) {
20780         if (typeof(this.groups[navId]) == 'undefined') {
20781             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20782         }
20783         return this.groups[navId] ;
20784     }
20785     
20786     
20787     
20788 });
20789
20790  /*
20791  * - LGPL
20792  *
20793  * TabPanel
20794  * 
20795  */
20796
20797 /**
20798  * @class Roo.bootstrap.TabPanel
20799  * @extends Roo.bootstrap.Component
20800  * Bootstrap TabPanel class
20801  * @cfg {Boolean} active panel active
20802  * @cfg {String} html panel content
20803  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20804  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20805  * @cfg {String} href click to link..
20806  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20807  * 
20808  * 
20809  * @constructor
20810  * Create a new TabPanel
20811  * @param {Object} config The config object
20812  */
20813
20814 Roo.bootstrap.TabPanel = function(config){
20815     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20816     this.addEvents({
20817         /**
20818              * @event changed
20819              * Fires when the active status changes
20820              * @param {Roo.bootstrap.TabPanel} this
20821              * @param {Boolean} state the new state
20822             
20823          */
20824         'changed': true,
20825         /**
20826              * @event beforedeactivate
20827              * Fires before a tab is de-activated - can be used to do validation on a form.
20828              * @param {Roo.bootstrap.TabPanel} this
20829              * @return {Boolean} false if there is an error
20830             
20831          */
20832         'beforedeactivate': true
20833      });
20834     
20835     this.tabId = this.tabId || Roo.id();
20836   
20837 };
20838
20839 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20840     
20841     active: false,
20842     html: false,
20843     tabId: false,
20844     navId : false,
20845     href : '',
20846     touchSlide : false,
20847     getAutoCreate : function(){
20848         
20849         
20850         var cfg = {
20851             tag: 'div',
20852             // item is needed for carousel - not sure if it has any effect otherwise
20853             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20854             html: this.html || ''
20855         };
20856         
20857         if(this.active){
20858             cfg.cls += ' active';
20859         }
20860         
20861         if(this.tabId){
20862             cfg.tabId = this.tabId;
20863         }
20864         
20865         
20866         
20867         return cfg;
20868     },
20869     
20870     initEvents:  function()
20871     {
20872         var p = this.parent();
20873         
20874         this.navId = this.navId || p.navId;
20875         
20876         if (typeof(this.navId) != 'undefined') {
20877             // not really needed.. but just in case.. parent should be a NavGroup.
20878             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20879             
20880             tg.register(this);
20881             
20882             var i = tg.tabs.length - 1;
20883             
20884             if(this.active && tg.bullets > 0 && i < tg.bullets){
20885                 tg.setActiveBullet(i);
20886             }
20887         }
20888         
20889         this.el.on('click', this.onClick, this);
20890         
20891         if(Roo.isTouch && this.touchSlide){
20892             this.el.on("touchstart", this.onTouchStart, this);
20893             this.el.on("touchmove", this.onTouchMove, this);
20894             this.el.on("touchend", this.onTouchEnd, this);
20895         }
20896         
20897     },
20898     
20899     onRender : function(ct, position)
20900     {
20901         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20902     },
20903     
20904     setActive : function(state)
20905     {
20906         Roo.log("panel - set active " + this.tabId + "=" + state);
20907         
20908         this.active = state;
20909         if (!state) {
20910             this.el.removeClass('active');
20911             
20912         } else  if (!this.el.hasClass('active')) {
20913             this.el.addClass('active');
20914         }
20915         
20916         this.fireEvent('changed', this, state);
20917     },
20918     
20919     onClick : function(e)
20920     {
20921         e.preventDefault();
20922         
20923         if(!this.href.length){
20924             return;
20925         }
20926         
20927         window.location.href = this.href;
20928     },
20929     
20930     startX : 0,
20931     startY : 0,
20932     endX : 0,
20933     endY : 0,
20934     swiping : false,
20935     
20936     onTouchStart : function(e)
20937     {
20938         this.swiping = false;
20939         
20940         this.startX = e.browserEvent.touches[0].clientX;
20941         this.startY = e.browserEvent.touches[0].clientY;
20942     },
20943     
20944     onTouchMove : function(e)
20945     {
20946         this.swiping = true;
20947         
20948         this.endX = e.browserEvent.touches[0].clientX;
20949         this.endY = e.browserEvent.touches[0].clientY;
20950     },
20951     
20952     onTouchEnd : function(e)
20953     {
20954         if(!this.swiping){
20955             this.onClick(e);
20956             return;
20957         }
20958         
20959         var tabGroup = this.parent();
20960         
20961         if(this.endX > this.startX){ // swiping right
20962             tabGroup.showPanelPrev();
20963             return;
20964         }
20965         
20966         if(this.startX > this.endX){ // swiping left
20967             tabGroup.showPanelNext();
20968             return;
20969         }
20970     }
20971     
20972     
20973 });
20974  
20975
20976  
20977
20978  /*
20979  * - LGPL
20980  *
20981  * DateField
20982  * 
20983  */
20984
20985 /**
20986  * @class Roo.bootstrap.DateField
20987  * @extends Roo.bootstrap.Input
20988  * Bootstrap DateField class
20989  * @cfg {Number} weekStart default 0
20990  * @cfg {String} viewMode default empty, (months|years)
20991  * @cfg {String} minViewMode default empty, (months|years)
20992  * @cfg {Number} startDate default -Infinity
20993  * @cfg {Number} endDate default Infinity
20994  * @cfg {Boolean} todayHighlight default false
20995  * @cfg {Boolean} todayBtn default false
20996  * @cfg {Boolean} calendarWeeks default false
20997  * @cfg {Object} daysOfWeekDisabled default empty
20998  * @cfg {Boolean} singleMode default false (true | false)
20999  * 
21000  * @cfg {Boolean} keyboardNavigation default true
21001  * @cfg {String} language default en
21002  * 
21003  * @constructor
21004  * Create a new DateField
21005  * @param {Object} config The config object
21006  */
21007
21008 Roo.bootstrap.DateField = function(config){
21009     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21010      this.addEvents({
21011             /**
21012              * @event show
21013              * Fires when this field show.
21014              * @param {Roo.bootstrap.DateField} this
21015              * @param {Mixed} date The date value
21016              */
21017             show : true,
21018             /**
21019              * @event show
21020              * Fires when this field hide.
21021              * @param {Roo.bootstrap.DateField} this
21022              * @param {Mixed} date The date value
21023              */
21024             hide : true,
21025             /**
21026              * @event select
21027              * Fires when select a date.
21028              * @param {Roo.bootstrap.DateField} this
21029              * @param {Mixed} date The date value
21030              */
21031             select : true,
21032             /**
21033              * @event beforeselect
21034              * Fires when before select a date.
21035              * @param {Roo.bootstrap.DateField} this
21036              * @param {Mixed} date The date value
21037              */
21038             beforeselect : true
21039         });
21040 };
21041
21042 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21043     
21044     /**
21045      * @cfg {String} format
21046      * The default date format string which can be overriden for localization support.  The format must be
21047      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21048      */
21049     format : "m/d/y",
21050     /**
21051      * @cfg {String} altFormats
21052      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21053      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21054      */
21055     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21056     
21057     weekStart : 0,
21058     
21059     viewMode : '',
21060     
21061     minViewMode : '',
21062     
21063     todayHighlight : false,
21064     
21065     todayBtn: false,
21066     
21067     language: 'en',
21068     
21069     keyboardNavigation: true,
21070     
21071     calendarWeeks: false,
21072     
21073     startDate: -Infinity,
21074     
21075     endDate: Infinity,
21076     
21077     daysOfWeekDisabled: [],
21078     
21079     _events: [],
21080     
21081     singleMode : false,
21082     
21083     UTCDate: function()
21084     {
21085         return new Date(Date.UTC.apply(Date, arguments));
21086     },
21087     
21088     UTCToday: function()
21089     {
21090         var today = new Date();
21091         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21092     },
21093     
21094     getDate: function() {
21095             var d = this.getUTCDate();
21096             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21097     },
21098     
21099     getUTCDate: function() {
21100             return this.date;
21101     },
21102     
21103     setDate: function(d) {
21104             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21105     },
21106     
21107     setUTCDate: function(d) {
21108             this.date = d;
21109             this.setValue(this.formatDate(this.date));
21110     },
21111         
21112     onRender: function(ct, position)
21113     {
21114         
21115         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21116         
21117         this.language = this.language || 'en';
21118         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21119         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21120         
21121         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21122         this.format = this.format || 'm/d/y';
21123         this.isInline = false;
21124         this.isInput = true;
21125         this.component = this.el.select('.add-on', true).first() || false;
21126         this.component = (this.component && this.component.length === 0) ? false : this.component;
21127         this.hasInput = this.component && this.inputEl().length;
21128         
21129         if (typeof(this.minViewMode === 'string')) {
21130             switch (this.minViewMode) {
21131                 case 'months':
21132                     this.minViewMode = 1;
21133                     break;
21134                 case 'years':
21135                     this.minViewMode = 2;
21136                     break;
21137                 default:
21138                     this.minViewMode = 0;
21139                     break;
21140             }
21141         }
21142         
21143         if (typeof(this.viewMode === 'string')) {
21144             switch (this.viewMode) {
21145                 case 'months':
21146                     this.viewMode = 1;
21147                     break;
21148                 case 'years':
21149                     this.viewMode = 2;
21150                     break;
21151                 default:
21152                     this.viewMode = 0;
21153                     break;
21154             }
21155         }
21156                 
21157         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21158         
21159 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21160         
21161         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21162         
21163         this.picker().on('mousedown', this.onMousedown, this);
21164         this.picker().on('click', this.onClick, this);
21165         
21166         this.picker().addClass('datepicker-dropdown');
21167         
21168         this.startViewMode = this.viewMode;
21169         
21170         if(this.singleMode){
21171             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21172                 v.setVisibilityMode(Roo.Element.DISPLAY);
21173                 v.hide();
21174             });
21175             
21176             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21177                 v.setStyle('width', '189px');
21178             });
21179         }
21180         
21181         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21182             if(!this.calendarWeeks){
21183                 v.remove();
21184                 return;
21185             }
21186             
21187             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21188             v.attr('colspan', function(i, val){
21189                 return parseInt(val) + 1;
21190             });
21191         });
21192                         
21193         
21194         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21195         
21196         this.setStartDate(this.startDate);
21197         this.setEndDate(this.endDate);
21198         
21199         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21200         
21201         this.fillDow();
21202         this.fillMonths();
21203         this.update();
21204         this.showMode();
21205         
21206         if(this.isInline) {
21207             this.showPopup();
21208         }
21209     },
21210     
21211     picker : function()
21212     {
21213         return this.pickerEl;
21214 //        return this.el.select('.datepicker', true).first();
21215     },
21216     
21217     fillDow: function()
21218     {
21219         var dowCnt = this.weekStart;
21220         
21221         var dow = {
21222             tag: 'tr',
21223             cn: [
21224                 
21225             ]
21226         };
21227         
21228         if(this.calendarWeeks){
21229             dow.cn.push({
21230                 tag: 'th',
21231                 cls: 'cw',
21232                 html: '&nbsp;'
21233             })
21234         }
21235         
21236         while (dowCnt < this.weekStart + 7) {
21237             dow.cn.push({
21238                 tag: 'th',
21239                 cls: 'dow',
21240                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21241             });
21242         }
21243         
21244         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21245     },
21246     
21247     fillMonths: function()
21248     {    
21249         var i = 0;
21250         var months = this.picker().select('>.datepicker-months td', true).first();
21251         
21252         months.dom.innerHTML = '';
21253         
21254         while (i < 12) {
21255             var month = {
21256                 tag: 'span',
21257                 cls: 'month',
21258                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21259             };
21260             
21261             months.createChild(month);
21262         }
21263         
21264     },
21265     
21266     update: function()
21267     {
21268         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;
21269         
21270         if (this.date < this.startDate) {
21271             this.viewDate = new Date(this.startDate);
21272         } else if (this.date > this.endDate) {
21273             this.viewDate = new Date(this.endDate);
21274         } else {
21275             this.viewDate = new Date(this.date);
21276         }
21277         
21278         this.fill();
21279     },
21280     
21281     fill: function() 
21282     {
21283         var d = new Date(this.viewDate),
21284                 year = d.getUTCFullYear(),
21285                 month = d.getUTCMonth(),
21286                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21287                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21288                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21289                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21290                 currentDate = this.date && this.date.valueOf(),
21291                 today = this.UTCToday();
21292         
21293         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21294         
21295 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21296         
21297 //        this.picker.select('>tfoot th.today').
21298 //                                              .text(dates[this.language].today)
21299 //                                              .toggle(this.todayBtn !== false);
21300     
21301         this.updateNavArrows();
21302         this.fillMonths();
21303                                                 
21304         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21305         
21306         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21307          
21308         prevMonth.setUTCDate(day);
21309         
21310         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21311         
21312         var nextMonth = new Date(prevMonth);
21313         
21314         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21315         
21316         nextMonth = nextMonth.valueOf();
21317         
21318         var fillMonths = false;
21319         
21320         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21321         
21322         while(prevMonth.valueOf() <= nextMonth) {
21323             var clsName = '';
21324             
21325             if (prevMonth.getUTCDay() === this.weekStart) {
21326                 if(fillMonths){
21327                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21328                 }
21329                     
21330                 fillMonths = {
21331                     tag: 'tr',
21332                     cn: []
21333                 };
21334                 
21335                 if(this.calendarWeeks){
21336                     // ISO 8601: First week contains first thursday.
21337                     // ISO also states week starts on Monday, but we can be more abstract here.
21338                     var
21339                     // Start of current week: based on weekstart/current date
21340                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21341                     // Thursday of this week
21342                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21343                     // First Thursday of year, year from thursday
21344                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21345                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21346                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21347                     
21348                     fillMonths.cn.push({
21349                         tag: 'td',
21350                         cls: 'cw',
21351                         html: calWeek
21352                     });
21353                 }
21354             }
21355             
21356             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21357                 clsName += ' old';
21358             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21359                 clsName += ' new';
21360             }
21361             if (this.todayHighlight &&
21362                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21363                 prevMonth.getUTCMonth() == today.getMonth() &&
21364                 prevMonth.getUTCDate() == today.getDate()) {
21365                 clsName += ' today';
21366             }
21367             
21368             if (currentDate && prevMonth.valueOf() === currentDate) {
21369                 clsName += ' active';
21370             }
21371             
21372             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21373                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21374                     clsName += ' disabled';
21375             }
21376             
21377             fillMonths.cn.push({
21378                 tag: 'td',
21379                 cls: 'day ' + clsName,
21380                 html: prevMonth.getDate()
21381             });
21382             
21383             prevMonth.setDate(prevMonth.getDate()+1);
21384         }
21385           
21386         var currentYear = this.date && this.date.getUTCFullYear();
21387         var currentMonth = this.date && this.date.getUTCMonth();
21388         
21389         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21390         
21391         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21392             v.removeClass('active');
21393             
21394             if(currentYear === year && k === currentMonth){
21395                 v.addClass('active');
21396             }
21397             
21398             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21399                 v.addClass('disabled');
21400             }
21401             
21402         });
21403         
21404         
21405         year = parseInt(year/10, 10) * 10;
21406         
21407         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21408         
21409         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21410         
21411         year -= 1;
21412         for (var i = -1; i < 11; i++) {
21413             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21414                 tag: 'span',
21415                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21416                 html: year
21417             });
21418             
21419             year += 1;
21420         }
21421     },
21422     
21423     showMode: function(dir) 
21424     {
21425         if (dir) {
21426             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21427         }
21428         
21429         Roo.each(this.picker().select('>div',true).elements, function(v){
21430             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21431             v.hide();
21432         });
21433         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21434     },
21435     
21436     place: function()
21437     {
21438         if(this.isInline) {
21439             return;
21440         }
21441         
21442         this.picker().removeClass(['bottom', 'top']);
21443         
21444         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21445             /*
21446              * place to the top of element!
21447              *
21448              */
21449             
21450             this.picker().addClass('top');
21451             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21452             
21453             return;
21454         }
21455         
21456         this.picker().addClass('bottom');
21457         
21458         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21459     },
21460     
21461     parseDate : function(value)
21462     {
21463         if(!value || value instanceof Date){
21464             return value;
21465         }
21466         var v = Date.parseDate(value, this.format);
21467         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21468             v = Date.parseDate(value, 'Y-m-d');
21469         }
21470         if(!v && this.altFormats){
21471             if(!this.altFormatsArray){
21472                 this.altFormatsArray = this.altFormats.split("|");
21473             }
21474             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21475                 v = Date.parseDate(value, this.altFormatsArray[i]);
21476             }
21477         }
21478         return v;
21479     },
21480     
21481     formatDate : function(date, fmt)
21482     {   
21483         return (!date || !(date instanceof Date)) ?
21484         date : date.dateFormat(fmt || this.format);
21485     },
21486     
21487     onFocus : function()
21488     {
21489         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21490         this.showPopup();
21491     },
21492     
21493     onBlur : function()
21494     {
21495         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21496         
21497         var d = this.inputEl().getValue();
21498         
21499         this.setValue(d);
21500                 
21501         this.hidePopup();
21502     },
21503     
21504     showPopup : function()
21505     {
21506         this.picker().show();
21507         this.update();
21508         this.place();
21509         
21510         this.fireEvent('showpopup', this, this.date);
21511     },
21512     
21513     hidePopup : function()
21514     {
21515         if(this.isInline) {
21516             return;
21517         }
21518         this.picker().hide();
21519         this.viewMode = this.startViewMode;
21520         this.showMode();
21521         
21522         this.fireEvent('hidepopup', this, this.date);
21523         
21524     },
21525     
21526     onMousedown: function(e)
21527     {
21528         e.stopPropagation();
21529         e.preventDefault();
21530     },
21531     
21532     keyup: function(e)
21533     {
21534         Roo.bootstrap.DateField.superclass.keyup.call(this);
21535         this.update();
21536     },
21537
21538     setValue: function(v)
21539     {
21540         if(this.fireEvent('beforeselect', this, v) !== false){
21541             var d = new Date(this.parseDate(v) ).clearTime();
21542         
21543             if(isNaN(d.getTime())){
21544                 this.date = this.viewDate = '';
21545                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21546                 return;
21547             }
21548
21549             v = this.formatDate(d);
21550
21551             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21552
21553             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21554
21555             this.update();
21556
21557             this.fireEvent('select', this, this.date);
21558         }
21559     },
21560     
21561     getValue: function()
21562     {
21563         return this.formatDate(this.date);
21564     },
21565     
21566     fireKey: function(e)
21567     {
21568         if (!this.picker().isVisible()){
21569             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21570                 this.showPopup();
21571             }
21572             return;
21573         }
21574         
21575         var dateChanged = false,
21576         dir, day, month,
21577         newDate, newViewDate;
21578         
21579         switch(e.keyCode){
21580             case 27: // escape
21581                 this.hidePopup();
21582                 e.preventDefault();
21583                 break;
21584             case 37: // left
21585             case 39: // right
21586                 if (!this.keyboardNavigation) {
21587                     break;
21588                 }
21589                 dir = e.keyCode == 37 ? -1 : 1;
21590                 
21591                 if (e.ctrlKey){
21592                     newDate = this.moveYear(this.date, dir);
21593                     newViewDate = this.moveYear(this.viewDate, dir);
21594                 } else if (e.shiftKey){
21595                     newDate = this.moveMonth(this.date, dir);
21596                     newViewDate = this.moveMonth(this.viewDate, dir);
21597                 } else {
21598                     newDate = new Date(this.date);
21599                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21600                     newViewDate = new Date(this.viewDate);
21601                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21602                 }
21603                 if (this.dateWithinRange(newDate)){
21604                     this.date = newDate;
21605                     this.viewDate = newViewDate;
21606                     this.setValue(this.formatDate(this.date));
21607 //                    this.update();
21608                     e.preventDefault();
21609                     dateChanged = true;
21610                 }
21611                 break;
21612             case 38: // up
21613             case 40: // down
21614                 if (!this.keyboardNavigation) {
21615                     break;
21616                 }
21617                 dir = e.keyCode == 38 ? -1 : 1;
21618                 if (e.ctrlKey){
21619                     newDate = this.moveYear(this.date, dir);
21620                     newViewDate = this.moveYear(this.viewDate, dir);
21621                 } else if (e.shiftKey){
21622                     newDate = this.moveMonth(this.date, dir);
21623                     newViewDate = this.moveMonth(this.viewDate, dir);
21624                 } else {
21625                     newDate = new Date(this.date);
21626                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21627                     newViewDate = new Date(this.viewDate);
21628                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21629                 }
21630                 if (this.dateWithinRange(newDate)){
21631                     this.date = newDate;
21632                     this.viewDate = newViewDate;
21633                     this.setValue(this.formatDate(this.date));
21634 //                    this.update();
21635                     e.preventDefault();
21636                     dateChanged = true;
21637                 }
21638                 break;
21639             case 13: // enter
21640                 this.setValue(this.formatDate(this.date));
21641                 this.hidePopup();
21642                 e.preventDefault();
21643                 break;
21644             case 9: // tab
21645                 this.setValue(this.formatDate(this.date));
21646                 this.hidePopup();
21647                 break;
21648             case 16: // shift
21649             case 17: // ctrl
21650             case 18: // alt
21651                 break;
21652             default :
21653                 this.hidePopup();
21654                 
21655         }
21656     },
21657     
21658     
21659     onClick: function(e) 
21660     {
21661         e.stopPropagation();
21662         e.preventDefault();
21663         
21664         var target = e.getTarget();
21665         
21666         if(target.nodeName.toLowerCase() === 'i'){
21667             target = Roo.get(target).dom.parentNode;
21668         }
21669         
21670         var nodeName = target.nodeName;
21671         var className = target.className;
21672         var html = target.innerHTML;
21673         //Roo.log(nodeName);
21674         
21675         switch(nodeName.toLowerCase()) {
21676             case 'th':
21677                 switch(className) {
21678                     case 'switch':
21679                         this.showMode(1);
21680                         break;
21681                     case 'prev':
21682                     case 'next':
21683                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21684                         switch(this.viewMode){
21685                                 case 0:
21686                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21687                                         break;
21688                                 case 1:
21689                                 case 2:
21690                                         this.viewDate = this.moveYear(this.viewDate, dir);
21691                                         break;
21692                         }
21693                         this.fill();
21694                         break;
21695                     case 'today':
21696                         var date = new Date();
21697                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21698 //                        this.fill()
21699                         this.setValue(this.formatDate(this.date));
21700                         
21701                         this.hidePopup();
21702                         break;
21703                 }
21704                 break;
21705             case 'span':
21706                 if (className.indexOf('disabled') < 0) {
21707                     this.viewDate.setUTCDate(1);
21708                     if (className.indexOf('month') > -1) {
21709                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21710                     } else {
21711                         var year = parseInt(html, 10) || 0;
21712                         this.viewDate.setUTCFullYear(year);
21713                         
21714                     }
21715                     
21716                     if(this.singleMode){
21717                         this.setValue(this.formatDate(this.viewDate));
21718                         this.hidePopup();
21719                         return;
21720                     }
21721                     
21722                     this.showMode(-1);
21723                     this.fill();
21724                 }
21725                 break;
21726                 
21727             case 'td':
21728                 //Roo.log(className);
21729                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21730                     var day = parseInt(html, 10) || 1;
21731                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21732                         month = (this.viewDate || new Date()).getUTCMonth();
21733
21734                     if (className.indexOf('old') > -1) {
21735                         if(month === 0 ){
21736                             month = 11;
21737                             year -= 1;
21738                         }else{
21739                             month -= 1;
21740                         }
21741                     } else if (className.indexOf('new') > -1) {
21742                         if (month == 11) {
21743                             month = 0;
21744                             year += 1;
21745                         } else {
21746                             month += 1;
21747                         }
21748                     }
21749                     //Roo.log([year,month,day]);
21750                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21751                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21752 //                    this.fill();
21753                     //Roo.log(this.formatDate(this.date));
21754                     this.setValue(this.formatDate(this.date));
21755                     this.hidePopup();
21756                 }
21757                 break;
21758         }
21759     },
21760     
21761     setStartDate: function(startDate)
21762     {
21763         this.startDate = startDate || -Infinity;
21764         if (this.startDate !== -Infinity) {
21765             this.startDate = this.parseDate(this.startDate);
21766         }
21767         this.update();
21768         this.updateNavArrows();
21769     },
21770
21771     setEndDate: function(endDate)
21772     {
21773         this.endDate = endDate || Infinity;
21774         if (this.endDate !== Infinity) {
21775             this.endDate = this.parseDate(this.endDate);
21776         }
21777         this.update();
21778         this.updateNavArrows();
21779     },
21780     
21781     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21782     {
21783         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21784         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21785             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21786         }
21787         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21788             return parseInt(d, 10);
21789         });
21790         this.update();
21791         this.updateNavArrows();
21792     },
21793     
21794     updateNavArrows: function() 
21795     {
21796         if(this.singleMode){
21797             return;
21798         }
21799         
21800         var d = new Date(this.viewDate),
21801         year = d.getUTCFullYear(),
21802         month = d.getUTCMonth();
21803         
21804         Roo.each(this.picker().select('.prev', true).elements, function(v){
21805             v.show();
21806             switch (this.viewMode) {
21807                 case 0:
21808
21809                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21810                         v.hide();
21811                     }
21812                     break;
21813                 case 1:
21814                 case 2:
21815                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21816                         v.hide();
21817                     }
21818                     break;
21819             }
21820         });
21821         
21822         Roo.each(this.picker().select('.next', true).elements, function(v){
21823             v.show();
21824             switch (this.viewMode) {
21825                 case 0:
21826
21827                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21828                         v.hide();
21829                     }
21830                     break;
21831                 case 1:
21832                 case 2:
21833                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21834                         v.hide();
21835                     }
21836                     break;
21837             }
21838         })
21839     },
21840     
21841     moveMonth: function(date, dir)
21842     {
21843         if (!dir) {
21844             return date;
21845         }
21846         var new_date = new Date(date.valueOf()),
21847         day = new_date.getUTCDate(),
21848         month = new_date.getUTCMonth(),
21849         mag = Math.abs(dir),
21850         new_month, test;
21851         dir = dir > 0 ? 1 : -1;
21852         if (mag == 1){
21853             test = dir == -1
21854             // If going back one month, make sure month is not current month
21855             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21856             ? function(){
21857                 return new_date.getUTCMonth() == month;
21858             }
21859             // If going forward one month, make sure month is as expected
21860             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21861             : function(){
21862                 return new_date.getUTCMonth() != new_month;
21863             };
21864             new_month = month + dir;
21865             new_date.setUTCMonth(new_month);
21866             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21867             if (new_month < 0 || new_month > 11) {
21868                 new_month = (new_month + 12) % 12;
21869             }
21870         } else {
21871             // For magnitudes >1, move one month at a time...
21872             for (var i=0; i<mag; i++) {
21873                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21874                 new_date = this.moveMonth(new_date, dir);
21875             }
21876             // ...then reset the day, keeping it in the new month
21877             new_month = new_date.getUTCMonth();
21878             new_date.setUTCDate(day);
21879             test = function(){
21880                 return new_month != new_date.getUTCMonth();
21881             };
21882         }
21883         // Common date-resetting loop -- if date is beyond end of month, make it
21884         // end of month
21885         while (test()){
21886             new_date.setUTCDate(--day);
21887             new_date.setUTCMonth(new_month);
21888         }
21889         return new_date;
21890     },
21891
21892     moveYear: function(date, dir)
21893     {
21894         return this.moveMonth(date, dir*12);
21895     },
21896
21897     dateWithinRange: function(date)
21898     {
21899         return date >= this.startDate && date <= this.endDate;
21900     },
21901
21902     
21903     remove: function() 
21904     {
21905         this.picker().remove();
21906     },
21907     
21908     validateValue : function(value)
21909     {
21910         if(this.getVisibilityEl().hasClass('hidden')){
21911             return true;
21912         }
21913         
21914         if(value.length < 1)  {
21915             if(this.allowBlank){
21916                 return true;
21917             }
21918             return false;
21919         }
21920         
21921         if(value.length < this.minLength){
21922             return false;
21923         }
21924         if(value.length > this.maxLength){
21925             return false;
21926         }
21927         if(this.vtype){
21928             var vt = Roo.form.VTypes;
21929             if(!vt[this.vtype](value, this)){
21930                 return false;
21931             }
21932         }
21933         if(typeof this.validator == "function"){
21934             var msg = this.validator(value);
21935             if(msg !== true){
21936                 return false;
21937             }
21938         }
21939         
21940         if(this.regex && !this.regex.test(value)){
21941             return false;
21942         }
21943         
21944         if(typeof(this.parseDate(value)) == 'undefined'){
21945             return false;
21946         }
21947         
21948         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21949             return false;
21950         }      
21951         
21952         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21953             return false;
21954         } 
21955         
21956         
21957         return true;
21958     },
21959     
21960     reset : function()
21961     {
21962         this.date = this.viewDate = '';
21963         
21964         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21965     }
21966    
21967 });
21968
21969 Roo.apply(Roo.bootstrap.DateField,  {
21970     
21971     head : {
21972         tag: 'thead',
21973         cn: [
21974         {
21975             tag: 'tr',
21976             cn: [
21977             {
21978                 tag: 'th',
21979                 cls: 'prev',
21980                 html: '<i class="fa fa-arrow-left"/>'
21981             },
21982             {
21983                 tag: 'th',
21984                 cls: 'switch',
21985                 colspan: '5'
21986             },
21987             {
21988                 tag: 'th',
21989                 cls: 'next',
21990                 html: '<i class="fa fa-arrow-right"/>'
21991             }
21992
21993             ]
21994         }
21995         ]
21996     },
21997     
21998     content : {
21999         tag: 'tbody',
22000         cn: [
22001         {
22002             tag: 'tr',
22003             cn: [
22004             {
22005                 tag: 'td',
22006                 colspan: '7'
22007             }
22008             ]
22009         }
22010         ]
22011     },
22012     
22013     footer : {
22014         tag: 'tfoot',
22015         cn: [
22016         {
22017             tag: 'tr',
22018             cn: [
22019             {
22020                 tag: 'th',
22021                 colspan: '7',
22022                 cls: 'today'
22023             }
22024                     
22025             ]
22026         }
22027         ]
22028     },
22029     
22030     dates:{
22031         en: {
22032             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22033             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22034             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22035             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22036             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22037             today: "Today"
22038         }
22039     },
22040     
22041     modes: [
22042     {
22043         clsName: 'days',
22044         navFnc: 'Month',
22045         navStep: 1
22046     },
22047     {
22048         clsName: 'months',
22049         navFnc: 'FullYear',
22050         navStep: 1
22051     },
22052     {
22053         clsName: 'years',
22054         navFnc: 'FullYear',
22055         navStep: 10
22056     }]
22057 });
22058
22059 Roo.apply(Roo.bootstrap.DateField,  {
22060   
22061     template : {
22062         tag: 'div',
22063         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22064         cn: [
22065         {
22066             tag: 'div',
22067             cls: 'datepicker-days',
22068             cn: [
22069             {
22070                 tag: 'table',
22071                 cls: 'table-condensed',
22072                 cn:[
22073                 Roo.bootstrap.DateField.head,
22074                 {
22075                     tag: 'tbody'
22076                 },
22077                 Roo.bootstrap.DateField.footer
22078                 ]
22079             }
22080             ]
22081         },
22082         {
22083             tag: 'div',
22084             cls: 'datepicker-months',
22085             cn: [
22086             {
22087                 tag: 'table',
22088                 cls: 'table-condensed',
22089                 cn:[
22090                 Roo.bootstrap.DateField.head,
22091                 Roo.bootstrap.DateField.content,
22092                 Roo.bootstrap.DateField.footer
22093                 ]
22094             }
22095             ]
22096         },
22097         {
22098             tag: 'div',
22099             cls: 'datepicker-years',
22100             cn: [
22101             {
22102                 tag: 'table',
22103                 cls: 'table-condensed',
22104                 cn:[
22105                 Roo.bootstrap.DateField.head,
22106                 Roo.bootstrap.DateField.content,
22107                 Roo.bootstrap.DateField.footer
22108                 ]
22109             }
22110             ]
22111         }
22112         ]
22113     }
22114 });
22115
22116  
22117
22118  /*
22119  * - LGPL
22120  *
22121  * TimeField
22122  * 
22123  */
22124
22125 /**
22126  * @class Roo.bootstrap.TimeField
22127  * @extends Roo.bootstrap.Input
22128  * Bootstrap DateField class
22129  * 
22130  * 
22131  * @constructor
22132  * Create a new TimeField
22133  * @param {Object} config The config object
22134  */
22135
22136 Roo.bootstrap.TimeField = function(config){
22137     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22138     this.addEvents({
22139             /**
22140              * @event show
22141              * Fires when this field show.
22142              * @param {Roo.bootstrap.DateField} thisthis
22143              * @param {Mixed} date The date value
22144              */
22145             show : true,
22146             /**
22147              * @event show
22148              * Fires when this field hide.
22149              * @param {Roo.bootstrap.DateField} this
22150              * @param {Mixed} date The date value
22151              */
22152             hide : true,
22153             /**
22154              * @event select
22155              * Fires when select a date.
22156              * @param {Roo.bootstrap.DateField} this
22157              * @param {Mixed} date The date value
22158              */
22159             select : true
22160         });
22161 };
22162
22163 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22164     
22165     /**
22166      * @cfg {String} format
22167      * The default time format string which can be overriden for localization support.  The format must be
22168      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22169      */
22170     format : "H:i",
22171
22172     getAutoCreate : function()
22173     {
22174         this.after = '<i class="fa far fa-clock"></i>';
22175         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22176         
22177          
22178     },
22179     onRender: function(ct, position)
22180     {
22181         
22182         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22183                 
22184         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22185         
22186         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22187         
22188         this.pop = this.picker().select('>.datepicker-time',true).first();
22189         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22190         
22191         this.picker().on('mousedown', this.onMousedown, this);
22192         this.picker().on('click', this.onClick, this);
22193         
22194         this.picker().addClass('datepicker-dropdown');
22195     
22196         this.fillTime();
22197         this.update();
22198             
22199         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22200         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22201         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22202         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22203         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22204         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22205
22206     },
22207     
22208     fireKey: function(e){
22209         if (!this.picker().isVisible()){
22210             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22211                 this.show();
22212             }
22213             return;
22214         }
22215
22216         e.preventDefault();
22217         
22218         switch(e.keyCode){
22219             case 27: // escape
22220                 this.hide();
22221                 break;
22222             case 37: // left
22223             case 39: // right
22224                 this.onTogglePeriod();
22225                 break;
22226             case 38: // up
22227                 this.onIncrementMinutes();
22228                 break;
22229             case 40: // down
22230                 this.onDecrementMinutes();
22231                 break;
22232             case 13: // enter
22233             case 9: // tab
22234                 this.setTime();
22235                 break;
22236         }
22237     },
22238     
22239     onClick: function(e) {
22240         e.stopPropagation();
22241         e.preventDefault();
22242     },
22243     
22244     picker : function()
22245     {
22246         return this.pickerEl;
22247     },
22248     
22249     fillTime: function()
22250     {    
22251         var time = this.pop.select('tbody', true).first();
22252         
22253         time.dom.innerHTML = '';
22254         
22255         time.createChild({
22256             tag: 'tr',
22257             cn: [
22258                 {
22259                     tag: 'td',
22260                     cn: [
22261                         {
22262                             tag: 'a',
22263                             href: '#',
22264                             cls: 'btn',
22265                             cn: [
22266                                 {
22267                                     tag: 'i',
22268                                     cls: 'hours-up fa fas fa-chevron-up'
22269                                 }
22270                             ]
22271                         } 
22272                     ]
22273                 },
22274                 {
22275                     tag: 'td',
22276                     cls: 'separator'
22277                 },
22278                 {
22279                     tag: 'td',
22280                     cn: [
22281                         {
22282                             tag: 'a',
22283                             href: '#',
22284                             cls: 'btn',
22285                             cn: [
22286                                 {
22287                                     tag: 'i',
22288                                     cls: 'minutes-up fa fas fa-chevron-up'
22289                                 }
22290                             ]
22291                         }
22292                     ]
22293                 },
22294                 {
22295                     tag: 'td',
22296                     cls: 'separator'
22297                 }
22298             ]
22299         });
22300         
22301         time.createChild({
22302             tag: 'tr',
22303             cn: [
22304                 {
22305                     tag: 'td',
22306                     cn: [
22307                         {
22308                             tag: 'span',
22309                             cls: 'timepicker-hour',
22310                             html: '00'
22311                         }  
22312                     ]
22313                 },
22314                 {
22315                     tag: 'td',
22316                     cls: 'separator',
22317                     html: ':'
22318                 },
22319                 {
22320                     tag: 'td',
22321                     cn: [
22322                         {
22323                             tag: 'span',
22324                             cls: 'timepicker-minute',
22325                             html: '00'
22326                         }  
22327                     ]
22328                 },
22329                 {
22330                     tag: 'td',
22331                     cls: 'separator'
22332                 },
22333                 {
22334                     tag: 'td',
22335                     cn: [
22336                         {
22337                             tag: 'button',
22338                             type: 'button',
22339                             cls: 'btn btn-primary period',
22340                             html: 'AM'
22341                             
22342                         }
22343                     ]
22344                 }
22345             ]
22346         });
22347         
22348         time.createChild({
22349             tag: 'tr',
22350             cn: [
22351                 {
22352                     tag: 'td',
22353                     cn: [
22354                         {
22355                             tag: 'a',
22356                             href: '#',
22357                             cls: 'btn',
22358                             cn: [
22359                                 {
22360                                     tag: 'span',
22361                                     cls: 'hours-down fa fas fa-chevron-down'
22362                                 }
22363                             ]
22364                         }
22365                     ]
22366                 },
22367                 {
22368                     tag: 'td',
22369                     cls: 'separator'
22370                 },
22371                 {
22372                     tag: 'td',
22373                     cn: [
22374                         {
22375                             tag: 'a',
22376                             href: '#',
22377                             cls: 'btn',
22378                             cn: [
22379                                 {
22380                                     tag: 'span',
22381                                     cls: 'minutes-down fa fas fa-chevron-down'
22382                                 }
22383                             ]
22384                         }
22385                     ]
22386                 },
22387                 {
22388                     tag: 'td',
22389                     cls: 'separator'
22390                 }
22391             ]
22392         });
22393         
22394     },
22395     
22396     update: function()
22397     {
22398         
22399         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22400         
22401         this.fill();
22402     },
22403     
22404     fill: function() 
22405     {
22406         var hours = this.time.getHours();
22407         var minutes = this.time.getMinutes();
22408         var period = 'AM';
22409         
22410         if(hours > 11){
22411             period = 'PM';
22412         }
22413         
22414         if(hours == 0){
22415             hours = 12;
22416         }
22417         
22418         
22419         if(hours > 12){
22420             hours = hours - 12;
22421         }
22422         
22423         if(hours < 10){
22424             hours = '0' + hours;
22425         }
22426         
22427         if(minutes < 10){
22428             minutes = '0' + minutes;
22429         }
22430         
22431         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22432         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22433         this.pop.select('button', true).first().dom.innerHTML = period;
22434         
22435     },
22436     
22437     place: function()
22438     {   
22439         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22440         
22441         var cls = ['bottom'];
22442         
22443         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22444             cls.pop();
22445             cls.push('top');
22446         }
22447         
22448         cls.push('right');
22449         
22450         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22451             cls.pop();
22452             cls.push('left');
22453         }
22454         //this.picker().setXY(20000,20000);
22455         this.picker().addClass(cls.join('-'));
22456         
22457         var _this = this;
22458         
22459         Roo.each(cls, function(c){
22460             if(c == 'bottom'){
22461                 (function() {
22462                  //  
22463                 }).defer(200);
22464                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22465                 //_this.picker().setTop(_this.inputEl().getHeight());
22466                 return;
22467             }
22468             if(c == 'top'){
22469                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22470                 
22471                 //_this.picker().setTop(0 - _this.picker().getHeight());
22472                 return;
22473             }
22474             /*
22475             if(c == 'left'){
22476                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22477                 return;
22478             }
22479             if(c == 'right'){
22480                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22481                 return;
22482             }
22483             */
22484         });
22485         
22486     },
22487   
22488     onFocus : function()
22489     {
22490         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22491         this.show();
22492     },
22493     
22494     onBlur : function()
22495     {
22496         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22497         this.hide();
22498     },
22499     
22500     show : function()
22501     {
22502         this.picker().show();
22503         this.pop.show();
22504         this.update();
22505         this.place();
22506         
22507         this.fireEvent('show', this, this.date);
22508     },
22509     
22510     hide : function()
22511     {
22512         this.picker().hide();
22513         this.pop.hide();
22514         
22515         this.fireEvent('hide', this, this.date);
22516     },
22517     
22518     setTime : function()
22519     {
22520         this.hide();
22521         this.setValue(this.time.format(this.format));
22522         
22523         this.fireEvent('select', this, this.date);
22524         
22525         
22526     },
22527     
22528     onMousedown: function(e){
22529         e.stopPropagation();
22530         e.preventDefault();
22531     },
22532     
22533     onIncrementHours: function()
22534     {
22535         Roo.log('onIncrementHours');
22536         this.time = this.time.add(Date.HOUR, 1);
22537         this.update();
22538         
22539     },
22540     
22541     onDecrementHours: function()
22542     {
22543         Roo.log('onDecrementHours');
22544         this.time = this.time.add(Date.HOUR, -1);
22545         this.update();
22546     },
22547     
22548     onIncrementMinutes: function()
22549     {
22550         Roo.log('onIncrementMinutes');
22551         this.time = this.time.add(Date.MINUTE, 1);
22552         this.update();
22553     },
22554     
22555     onDecrementMinutes: function()
22556     {
22557         Roo.log('onDecrementMinutes');
22558         this.time = this.time.add(Date.MINUTE, -1);
22559         this.update();
22560     },
22561     
22562     onTogglePeriod: function()
22563     {
22564         Roo.log('onTogglePeriod');
22565         this.time = this.time.add(Date.HOUR, 12);
22566         this.update();
22567     }
22568     
22569    
22570 });
22571  
22572
22573 Roo.apply(Roo.bootstrap.TimeField,  {
22574   
22575     template : {
22576         tag: 'div',
22577         cls: 'datepicker dropdown-menu',
22578         cn: [
22579             {
22580                 tag: 'div',
22581                 cls: 'datepicker-time',
22582                 cn: [
22583                 {
22584                     tag: 'table',
22585                     cls: 'table-condensed',
22586                     cn:[
22587                         {
22588                             tag: 'tbody',
22589                             cn: [
22590                                 {
22591                                     tag: 'tr',
22592                                     cn: [
22593                                     {
22594                                         tag: 'td',
22595                                         colspan: '7'
22596                                     }
22597                                     ]
22598                                 }
22599                             ]
22600                         },
22601                         {
22602                             tag: 'tfoot',
22603                             cn: [
22604                                 {
22605                                     tag: 'tr',
22606                                     cn: [
22607                                     {
22608                                         tag: 'th',
22609                                         colspan: '7',
22610                                         cls: '',
22611                                         cn: [
22612                                             {
22613                                                 tag: 'button',
22614                                                 cls: 'btn btn-info ok',
22615                                                 html: 'OK'
22616                                             }
22617                                         ]
22618                                     }
22619                     
22620                                     ]
22621                                 }
22622                             ]
22623                         }
22624                     ]
22625                 }
22626                 ]
22627             }
22628         ]
22629     }
22630 });
22631
22632  
22633
22634  /*
22635  * - LGPL
22636  *
22637  * MonthField
22638  * 
22639  */
22640
22641 /**
22642  * @class Roo.bootstrap.MonthField
22643  * @extends Roo.bootstrap.Input
22644  * Bootstrap MonthField class
22645  * 
22646  * @cfg {String} language default en
22647  * 
22648  * @constructor
22649  * Create a new MonthField
22650  * @param {Object} config The config object
22651  */
22652
22653 Roo.bootstrap.MonthField = function(config){
22654     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22655     
22656     this.addEvents({
22657         /**
22658          * @event show
22659          * Fires when this field show.
22660          * @param {Roo.bootstrap.MonthField} this
22661          * @param {Mixed} date The date value
22662          */
22663         show : true,
22664         /**
22665          * @event show
22666          * Fires when this field hide.
22667          * @param {Roo.bootstrap.MonthField} this
22668          * @param {Mixed} date The date value
22669          */
22670         hide : true,
22671         /**
22672          * @event select
22673          * Fires when select a date.
22674          * @param {Roo.bootstrap.MonthField} this
22675          * @param {String} oldvalue The old value
22676          * @param {String} newvalue The new value
22677          */
22678         select : true
22679     });
22680 };
22681
22682 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22683     
22684     onRender: function(ct, position)
22685     {
22686         
22687         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22688         
22689         this.language = this.language || 'en';
22690         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22691         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22692         
22693         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22694         this.isInline = false;
22695         this.isInput = true;
22696         this.component = this.el.select('.add-on', true).first() || false;
22697         this.component = (this.component && this.component.length === 0) ? false : this.component;
22698         this.hasInput = this.component && this.inputEL().length;
22699         
22700         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22701         
22702         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22703         
22704         this.picker().on('mousedown', this.onMousedown, this);
22705         this.picker().on('click', this.onClick, this);
22706         
22707         this.picker().addClass('datepicker-dropdown');
22708         
22709         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22710             v.setStyle('width', '189px');
22711         });
22712         
22713         this.fillMonths();
22714         
22715         this.update();
22716         
22717         if(this.isInline) {
22718             this.show();
22719         }
22720         
22721     },
22722     
22723     setValue: function(v, suppressEvent)
22724     {   
22725         var o = this.getValue();
22726         
22727         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22728         
22729         this.update();
22730
22731         if(suppressEvent !== true){
22732             this.fireEvent('select', this, o, v);
22733         }
22734         
22735     },
22736     
22737     getValue: function()
22738     {
22739         return this.value;
22740     },
22741     
22742     onClick: function(e) 
22743     {
22744         e.stopPropagation();
22745         e.preventDefault();
22746         
22747         var target = e.getTarget();
22748         
22749         if(target.nodeName.toLowerCase() === 'i'){
22750             target = Roo.get(target).dom.parentNode;
22751         }
22752         
22753         var nodeName = target.nodeName;
22754         var className = target.className;
22755         var html = target.innerHTML;
22756         
22757         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22758             return;
22759         }
22760         
22761         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22762         
22763         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22764         
22765         this.hide();
22766                         
22767     },
22768     
22769     picker : function()
22770     {
22771         return this.pickerEl;
22772     },
22773     
22774     fillMonths: function()
22775     {    
22776         var i = 0;
22777         var months = this.picker().select('>.datepicker-months td', true).first();
22778         
22779         months.dom.innerHTML = '';
22780         
22781         while (i < 12) {
22782             var month = {
22783                 tag: 'span',
22784                 cls: 'month',
22785                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22786             };
22787             
22788             months.createChild(month);
22789         }
22790         
22791     },
22792     
22793     update: function()
22794     {
22795         var _this = this;
22796         
22797         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22798             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22799         }
22800         
22801         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22802             e.removeClass('active');
22803             
22804             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22805                 e.addClass('active');
22806             }
22807         })
22808     },
22809     
22810     place: function()
22811     {
22812         if(this.isInline) {
22813             return;
22814         }
22815         
22816         this.picker().removeClass(['bottom', 'top']);
22817         
22818         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22819             /*
22820              * place to the top of element!
22821              *
22822              */
22823             
22824             this.picker().addClass('top');
22825             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22826             
22827             return;
22828         }
22829         
22830         this.picker().addClass('bottom');
22831         
22832         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22833     },
22834     
22835     onFocus : function()
22836     {
22837         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22838         this.show();
22839     },
22840     
22841     onBlur : function()
22842     {
22843         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22844         
22845         var d = this.inputEl().getValue();
22846         
22847         this.setValue(d);
22848                 
22849         this.hide();
22850     },
22851     
22852     show : function()
22853     {
22854         this.picker().show();
22855         this.picker().select('>.datepicker-months', true).first().show();
22856         this.update();
22857         this.place();
22858         
22859         this.fireEvent('show', this, this.date);
22860     },
22861     
22862     hide : function()
22863     {
22864         if(this.isInline) {
22865             return;
22866         }
22867         this.picker().hide();
22868         this.fireEvent('hide', this, this.date);
22869         
22870     },
22871     
22872     onMousedown: function(e)
22873     {
22874         e.stopPropagation();
22875         e.preventDefault();
22876     },
22877     
22878     keyup: function(e)
22879     {
22880         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22881         this.update();
22882     },
22883
22884     fireKey: function(e)
22885     {
22886         if (!this.picker().isVisible()){
22887             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22888                 this.show();
22889             }
22890             return;
22891         }
22892         
22893         var dir;
22894         
22895         switch(e.keyCode){
22896             case 27: // escape
22897                 this.hide();
22898                 e.preventDefault();
22899                 break;
22900             case 37: // left
22901             case 39: // right
22902                 dir = e.keyCode == 37 ? -1 : 1;
22903                 
22904                 this.vIndex = this.vIndex + dir;
22905                 
22906                 if(this.vIndex < 0){
22907                     this.vIndex = 0;
22908                 }
22909                 
22910                 if(this.vIndex > 11){
22911                     this.vIndex = 11;
22912                 }
22913                 
22914                 if(isNaN(this.vIndex)){
22915                     this.vIndex = 0;
22916                 }
22917                 
22918                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22919                 
22920                 break;
22921             case 38: // up
22922             case 40: // down
22923                 
22924                 dir = e.keyCode == 38 ? -1 : 1;
22925                 
22926                 this.vIndex = this.vIndex + dir * 4;
22927                 
22928                 if(this.vIndex < 0){
22929                     this.vIndex = 0;
22930                 }
22931                 
22932                 if(this.vIndex > 11){
22933                     this.vIndex = 11;
22934                 }
22935                 
22936                 if(isNaN(this.vIndex)){
22937                     this.vIndex = 0;
22938                 }
22939                 
22940                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22941                 break;
22942                 
22943             case 13: // enter
22944                 
22945                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22946                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22947                 }
22948                 
22949                 this.hide();
22950                 e.preventDefault();
22951                 break;
22952             case 9: // tab
22953                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22954                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22955                 }
22956                 this.hide();
22957                 break;
22958             case 16: // shift
22959             case 17: // ctrl
22960             case 18: // alt
22961                 break;
22962             default :
22963                 this.hide();
22964                 
22965         }
22966     },
22967     
22968     remove: function() 
22969     {
22970         this.picker().remove();
22971     }
22972    
22973 });
22974
22975 Roo.apply(Roo.bootstrap.MonthField,  {
22976     
22977     content : {
22978         tag: 'tbody',
22979         cn: [
22980         {
22981             tag: 'tr',
22982             cn: [
22983             {
22984                 tag: 'td',
22985                 colspan: '7'
22986             }
22987             ]
22988         }
22989         ]
22990     },
22991     
22992     dates:{
22993         en: {
22994             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22995             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22996         }
22997     }
22998 });
22999
23000 Roo.apply(Roo.bootstrap.MonthField,  {
23001   
23002     template : {
23003         tag: 'div',
23004         cls: 'datepicker dropdown-menu roo-dynamic',
23005         cn: [
23006             {
23007                 tag: 'div',
23008                 cls: 'datepicker-months',
23009                 cn: [
23010                 {
23011                     tag: 'table',
23012                     cls: 'table-condensed',
23013                     cn:[
23014                         Roo.bootstrap.DateField.content
23015                     ]
23016                 }
23017                 ]
23018             }
23019         ]
23020     }
23021 });
23022
23023  
23024
23025  
23026  /*
23027  * - LGPL
23028  *
23029  * CheckBox
23030  * 
23031  */
23032
23033 /**
23034  * @class Roo.bootstrap.CheckBox
23035  * @extends Roo.bootstrap.Input
23036  * Bootstrap CheckBox class
23037  * 
23038  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23039  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23040  * @cfg {String} boxLabel The text that appears beside the checkbox
23041  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23042  * @cfg {Boolean} checked initnal the element
23043  * @cfg {Boolean} inline inline the element (default false)
23044  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23045  * @cfg {String} tooltip label tooltip
23046  * 
23047  * @constructor
23048  * Create a new CheckBox
23049  * @param {Object} config The config object
23050  */
23051
23052 Roo.bootstrap.CheckBox = function(config){
23053     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23054    
23055     this.addEvents({
23056         /**
23057         * @event check
23058         * Fires when the element is checked or unchecked.
23059         * @param {Roo.bootstrap.CheckBox} this This input
23060         * @param {Boolean} checked The new checked value
23061         */
23062        check : true,
23063        /**
23064         * @event click
23065         * Fires when the element is click.
23066         * @param {Roo.bootstrap.CheckBox} this This input
23067         */
23068        click : true
23069     });
23070     
23071 };
23072
23073 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23074   
23075     inputType: 'checkbox',
23076     inputValue: 1,
23077     valueOff: 0,
23078     boxLabel: false,
23079     checked: false,
23080     weight : false,
23081     inline: false,
23082     tooltip : '',
23083     
23084     // checkbox success does not make any sense really.. 
23085     invalidClass : "",
23086     validClass : "",
23087     
23088     
23089     getAutoCreate : function()
23090     {
23091         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23092         
23093         var id = Roo.id();
23094         
23095         var cfg = {};
23096         
23097         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23098         
23099         if(this.inline){
23100             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23101         }
23102         
23103         var input =  {
23104             tag: 'input',
23105             id : id,
23106             type : this.inputType,
23107             value : this.inputValue,
23108             cls : 'roo-' + this.inputType, //'form-box',
23109             placeholder : this.placeholder || ''
23110             
23111         };
23112         
23113         if(this.inputType != 'radio'){
23114             var hidden =  {
23115                 tag: 'input',
23116                 type : 'hidden',
23117                 cls : 'roo-hidden-value',
23118                 value : this.checked ? this.inputValue : this.valueOff
23119             };
23120         }
23121         
23122             
23123         if (this.weight) { // Validity check?
23124             cfg.cls += " " + this.inputType + "-" + this.weight;
23125         }
23126         
23127         if (this.disabled) {
23128             input.disabled=true;
23129         }
23130         
23131         if(this.checked){
23132             input.checked = this.checked;
23133         }
23134         
23135         if (this.name) {
23136             
23137             input.name = this.name;
23138             
23139             if(this.inputType != 'radio'){
23140                 hidden.name = this.name;
23141                 input.name = '_hidden_' + this.name;
23142             }
23143         }
23144         
23145         if (this.size) {
23146             input.cls += ' input-' + this.size;
23147         }
23148         
23149         var settings=this;
23150         
23151         ['xs','sm','md','lg'].map(function(size){
23152             if (settings[size]) {
23153                 cfg.cls += ' col-' + size + '-' + settings[size];
23154             }
23155         });
23156         
23157         var inputblock = input;
23158          
23159         if (this.before || this.after) {
23160             
23161             inputblock = {
23162                 cls : 'input-group',
23163                 cn :  [] 
23164             };
23165             
23166             if (this.before) {
23167                 inputblock.cn.push({
23168                     tag :'span',
23169                     cls : 'input-group-addon',
23170                     html : this.before
23171                 });
23172             }
23173             
23174             inputblock.cn.push(input);
23175             
23176             if(this.inputType != 'radio'){
23177                 inputblock.cn.push(hidden);
23178             }
23179             
23180             if (this.after) {
23181                 inputblock.cn.push({
23182                     tag :'span',
23183                     cls : 'input-group-addon',
23184                     html : this.after
23185                 });
23186             }
23187             
23188         }
23189         var boxLabelCfg = false;
23190         
23191         if(this.boxLabel){
23192            
23193             boxLabelCfg = {
23194                 tag: 'label',
23195                 //'for': id, // box label is handled by onclick - so no for...
23196                 cls: 'box-label',
23197                 html: this.boxLabel
23198             };
23199             if(this.tooltip){
23200                 boxLabelCfg.tooltip = this.tooltip;
23201             }
23202              
23203         }
23204         
23205         
23206         if (align ==='left' && this.fieldLabel.length) {
23207 //                Roo.log("left and has label");
23208             cfg.cn = [
23209                 {
23210                     tag: 'label',
23211                     'for' :  id,
23212                     cls : 'control-label',
23213                     html : this.fieldLabel
23214                 },
23215                 {
23216                     cls : "", 
23217                     cn: [
23218                         inputblock
23219                     ]
23220                 }
23221             ];
23222             
23223             if (boxLabelCfg) {
23224                 cfg.cn[1].cn.push(boxLabelCfg);
23225             }
23226             
23227             if(this.labelWidth > 12){
23228                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23229             }
23230             
23231             if(this.labelWidth < 13 && this.labelmd == 0){
23232                 this.labelmd = this.labelWidth;
23233             }
23234             
23235             if(this.labellg > 0){
23236                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23237                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23238             }
23239             
23240             if(this.labelmd > 0){
23241                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23242                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23243             }
23244             
23245             if(this.labelsm > 0){
23246                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23247                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23248             }
23249             
23250             if(this.labelxs > 0){
23251                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23252                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23253             }
23254             
23255         } else if ( this.fieldLabel.length) {
23256 //                Roo.log(" label");
23257                 cfg.cn = [
23258                    
23259                     {
23260                         tag: this.boxLabel ? 'span' : 'label',
23261                         'for': id,
23262                         cls: 'control-label box-input-label',
23263                         //cls : 'input-group-addon',
23264                         html : this.fieldLabel
23265                     },
23266                     
23267                     inputblock
23268                     
23269                 ];
23270                 if (boxLabelCfg) {
23271                     cfg.cn.push(boxLabelCfg);
23272                 }
23273
23274         } else {
23275             
23276 //                Roo.log(" no label && no align");
23277                 cfg.cn = [  inputblock ] ;
23278                 if (boxLabelCfg) {
23279                     cfg.cn.push(boxLabelCfg);
23280                 }
23281
23282                 
23283         }
23284         
23285        
23286         
23287         if(this.inputType != 'radio'){
23288             cfg.cn.push(hidden);
23289         }
23290         
23291         return cfg;
23292         
23293     },
23294     
23295     /**
23296      * return the real input element.
23297      */
23298     inputEl: function ()
23299     {
23300         return this.el.select('input.roo-' + this.inputType,true).first();
23301     },
23302     hiddenEl: function ()
23303     {
23304         return this.el.select('input.roo-hidden-value',true).first();
23305     },
23306     
23307     labelEl: function()
23308     {
23309         return this.el.select('label.control-label',true).first();
23310     },
23311     /* depricated... */
23312     
23313     label: function()
23314     {
23315         return this.labelEl();
23316     },
23317     
23318     boxLabelEl: function()
23319     {
23320         return this.el.select('label.box-label',true).first();
23321     },
23322     
23323     initEvents : function()
23324     {
23325 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23326         
23327         this.inputEl().on('click', this.onClick,  this);
23328         
23329         if (this.boxLabel) { 
23330             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23331         }
23332         
23333         this.startValue = this.getValue();
23334         
23335         if(this.groupId){
23336             Roo.bootstrap.CheckBox.register(this);
23337         }
23338     },
23339     
23340     onClick : function(e)
23341     {   
23342         if(this.fireEvent('click', this, e) !== false){
23343             this.setChecked(!this.checked);
23344         }
23345         
23346     },
23347     
23348     setChecked : function(state,suppressEvent)
23349     {
23350         this.startValue = this.getValue();
23351
23352         if(this.inputType == 'radio'){
23353             
23354             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23355                 e.dom.checked = false;
23356             });
23357             
23358             this.inputEl().dom.checked = true;
23359             
23360             this.inputEl().dom.value = this.inputValue;
23361             
23362             if(suppressEvent !== true){
23363                 this.fireEvent('check', this, true);
23364             }
23365             
23366             this.validate();
23367             
23368             return;
23369         }
23370         
23371         this.checked = state;
23372         
23373         this.inputEl().dom.checked = state;
23374         
23375         
23376         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23377         
23378         if(suppressEvent !== true){
23379             this.fireEvent('check', this, state);
23380         }
23381         
23382         this.validate();
23383     },
23384     
23385     getValue : function()
23386     {
23387         if(this.inputType == 'radio'){
23388             return this.getGroupValue();
23389         }
23390         
23391         return this.hiddenEl().dom.value;
23392         
23393     },
23394     
23395     getGroupValue : function()
23396     {
23397         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23398             return '';
23399         }
23400         
23401         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23402     },
23403     
23404     setValue : function(v,suppressEvent)
23405     {
23406         if(this.inputType == 'radio'){
23407             this.setGroupValue(v, suppressEvent);
23408             return;
23409         }
23410         
23411         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23412         
23413         this.validate();
23414     },
23415     
23416     setGroupValue : function(v, suppressEvent)
23417     {
23418         this.startValue = this.getValue();
23419         
23420         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23421             e.dom.checked = false;
23422             
23423             if(e.dom.value == v){
23424                 e.dom.checked = true;
23425             }
23426         });
23427         
23428         if(suppressEvent !== true){
23429             this.fireEvent('check', this, true);
23430         }
23431
23432         this.validate();
23433         
23434         return;
23435     },
23436     
23437     validate : function()
23438     {
23439         if(this.getVisibilityEl().hasClass('hidden')){
23440             return true;
23441         }
23442         
23443         if(
23444                 this.disabled || 
23445                 (this.inputType == 'radio' && this.validateRadio()) ||
23446                 (this.inputType == 'checkbox' && this.validateCheckbox())
23447         ){
23448             this.markValid();
23449             return true;
23450         }
23451         
23452         this.markInvalid();
23453         return false;
23454     },
23455     
23456     validateRadio : function()
23457     {
23458         if(this.getVisibilityEl().hasClass('hidden')){
23459             return true;
23460         }
23461         
23462         if(this.allowBlank){
23463             return true;
23464         }
23465         
23466         var valid = false;
23467         
23468         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23469             if(!e.dom.checked){
23470                 return;
23471             }
23472             
23473             valid = true;
23474             
23475             return false;
23476         });
23477         
23478         return valid;
23479     },
23480     
23481     validateCheckbox : function()
23482     {
23483         if(!this.groupId){
23484             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23485             //return (this.getValue() == this.inputValue) ? true : false;
23486         }
23487         
23488         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23489         
23490         if(!group){
23491             return false;
23492         }
23493         
23494         var r = false;
23495         
23496         for(var i in group){
23497             if(group[i].el.isVisible(true)){
23498                 r = false;
23499                 break;
23500             }
23501             
23502             r = true;
23503         }
23504         
23505         for(var i in group){
23506             if(r){
23507                 break;
23508             }
23509             
23510             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23511         }
23512         
23513         return r;
23514     },
23515     
23516     /**
23517      * Mark this field as valid
23518      */
23519     markValid : function()
23520     {
23521         var _this = this;
23522         
23523         this.fireEvent('valid', this);
23524         
23525         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23526         
23527         if(this.groupId){
23528             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23529         }
23530         
23531         if(label){
23532             label.markValid();
23533         }
23534
23535         if(this.inputType == 'radio'){
23536             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23537                 var fg = e.findParent('.form-group', false, true);
23538                 if (Roo.bootstrap.version == 3) {
23539                     fg.removeClass([_this.invalidClass, _this.validClass]);
23540                     fg.addClass(_this.validClass);
23541                 } else {
23542                     fg.removeClass(['is-valid', 'is-invalid']);
23543                     fg.addClass('is-valid');
23544                 }
23545             });
23546             
23547             return;
23548         }
23549
23550         if(!this.groupId){
23551             var fg = this.el.findParent('.form-group', false, true);
23552             if (Roo.bootstrap.version == 3) {
23553                 fg.removeClass([this.invalidClass, this.validClass]);
23554                 fg.addClass(this.validClass);
23555             } else {
23556                 fg.removeClass(['is-valid', 'is-invalid']);
23557                 fg.addClass('is-valid');
23558             }
23559             return;
23560         }
23561         
23562         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23563         
23564         if(!group){
23565             return;
23566         }
23567         
23568         for(var i in group){
23569             var fg = group[i].el.findParent('.form-group', false, true);
23570             if (Roo.bootstrap.version == 3) {
23571                 fg.removeClass([this.invalidClass, this.validClass]);
23572                 fg.addClass(this.validClass);
23573             } else {
23574                 fg.removeClass(['is-valid', 'is-invalid']);
23575                 fg.addClass('is-valid');
23576             }
23577         }
23578     },
23579     
23580      /**
23581      * Mark this field as invalid
23582      * @param {String} msg The validation message
23583      */
23584     markInvalid : function(msg)
23585     {
23586         if(this.allowBlank){
23587             return;
23588         }
23589         
23590         var _this = this;
23591         
23592         this.fireEvent('invalid', this, msg);
23593         
23594         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23595         
23596         if(this.groupId){
23597             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23598         }
23599         
23600         if(label){
23601             label.markInvalid();
23602         }
23603             
23604         if(this.inputType == 'radio'){
23605             
23606             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23607                 var fg = e.findParent('.form-group', false, true);
23608                 if (Roo.bootstrap.version == 3) {
23609                     fg.removeClass([_this.invalidClass, _this.validClass]);
23610                     fg.addClass(_this.invalidClass);
23611                 } else {
23612                     fg.removeClass(['is-invalid', 'is-valid']);
23613                     fg.addClass('is-invalid');
23614                 }
23615             });
23616             
23617             return;
23618         }
23619         
23620         if(!this.groupId){
23621             var fg = this.el.findParent('.form-group', false, true);
23622             if (Roo.bootstrap.version == 3) {
23623                 fg.removeClass([_this.invalidClass, _this.validClass]);
23624                 fg.addClass(_this.invalidClass);
23625             } else {
23626                 fg.removeClass(['is-invalid', 'is-valid']);
23627                 fg.addClass('is-invalid');
23628             }
23629             return;
23630         }
23631         
23632         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23633         
23634         if(!group){
23635             return;
23636         }
23637         
23638         for(var i in group){
23639             var fg = group[i].el.findParent('.form-group', false, true);
23640             if (Roo.bootstrap.version == 3) {
23641                 fg.removeClass([_this.invalidClass, _this.validClass]);
23642                 fg.addClass(_this.invalidClass);
23643             } else {
23644                 fg.removeClass(['is-invalid', 'is-valid']);
23645                 fg.addClass('is-invalid');
23646             }
23647         }
23648         
23649     },
23650     
23651     clearInvalid : function()
23652     {
23653         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23654         
23655         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23656         
23657         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23658         
23659         if (label && label.iconEl) {
23660             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23661             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23662         }
23663     },
23664     
23665     disable : function()
23666     {
23667         if(this.inputType != 'radio'){
23668             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23669             return;
23670         }
23671         
23672         var _this = this;
23673         
23674         if(this.rendered){
23675             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23676                 _this.getActionEl().addClass(this.disabledClass);
23677                 e.dom.disabled = true;
23678             });
23679         }
23680         
23681         this.disabled = true;
23682         this.fireEvent("disable", this);
23683         return this;
23684     },
23685
23686     enable : function()
23687     {
23688         if(this.inputType != 'radio'){
23689             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23690             return;
23691         }
23692         
23693         var _this = this;
23694         
23695         if(this.rendered){
23696             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23697                 _this.getActionEl().removeClass(this.disabledClass);
23698                 e.dom.disabled = false;
23699             });
23700         }
23701         
23702         this.disabled = false;
23703         this.fireEvent("enable", this);
23704         return this;
23705     },
23706     
23707     setBoxLabel : function(v)
23708     {
23709         this.boxLabel = v;
23710         
23711         if(this.rendered){
23712             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23713         }
23714     }
23715
23716 });
23717
23718 Roo.apply(Roo.bootstrap.CheckBox, {
23719     
23720     groups: {},
23721     
23722      /**
23723     * register a CheckBox Group
23724     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23725     */
23726     register : function(checkbox)
23727     {
23728         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23729             this.groups[checkbox.groupId] = {};
23730         }
23731         
23732         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23733             return;
23734         }
23735         
23736         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23737         
23738     },
23739     /**
23740     * fetch a CheckBox Group based on the group ID
23741     * @param {string} the group ID
23742     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23743     */
23744     get: function(groupId) {
23745         if (typeof(this.groups[groupId]) == 'undefined') {
23746             return false;
23747         }
23748         
23749         return this.groups[groupId] ;
23750     }
23751     
23752     
23753 });
23754 /*
23755  * - LGPL
23756  *
23757  * RadioItem
23758  * 
23759  */
23760
23761 /**
23762  * @class Roo.bootstrap.Radio
23763  * @extends Roo.bootstrap.Component
23764  * Bootstrap Radio class
23765  * @cfg {String} boxLabel - the label associated
23766  * @cfg {String} value - the value of radio
23767  * 
23768  * @constructor
23769  * Create a new Radio
23770  * @param {Object} config The config object
23771  */
23772 Roo.bootstrap.Radio = function(config){
23773     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23774     
23775 };
23776
23777 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23778     
23779     boxLabel : '',
23780     
23781     value : '',
23782     
23783     getAutoCreate : function()
23784     {
23785         var cfg = {
23786             tag : 'div',
23787             cls : 'form-group radio',
23788             cn : [
23789                 {
23790                     tag : 'label',
23791                     cls : 'box-label',
23792                     html : this.boxLabel
23793                 }
23794             ]
23795         };
23796         
23797         return cfg;
23798     },
23799     
23800     initEvents : function() 
23801     {
23802         this.parent().register(this);
23803         
23804         this.el.on('click', this.onClick, this);
23805         
23806     },
23807     
23808     onClick : function(e)
23809     {
23810         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23811             this.setChecked(true);
23812         }
23813     },
23814     
23815     setChecked : function(state, suppressEvent)
23816     {
23817         this.parent().setValue(this.value, suppressEvent);
23818         
23819     },
23820     
23821     setBoxLabel : function(v)
23822     {
23823         this.boxLabel = v;
23824         
23825         if(this.rendered){
23826             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23827         }
23828     }
23829     
23830 });
23831  
23832
23833  /*
23834  * - LGPL
23835  *
23836  * Input
23837  * 
23838  */
23839
23840 /**
23841  * @class Roo.bootstrap.SecurePass
23842  * @extends Roo.bootstrap.Input
23843  * Bootstrap SecurePass class
23844  *
23845  * 
23846  * @constructor
23847  * Create a new SecurePass
23848  * @param {Object} config The config object
23849  */
23850  
23851 Roo.bootstrap.SecurePass = function (config) {
23852     // these go here, so the translation tool can replace them..
23853     this.errors = {
23854         PwdEmpty: "Please type a password, and then retype it to confirm.",
23855         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23856         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23857         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23858         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23859         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23860         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23861         TooWeak: "Your password is Too Weak."
23862     },
23863     this.meterLabel = "Password strength:";
23864     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23865     this.meterClass = [
23866         "roo-password-meter-tooweak", 
23867         "roo-password-meter-weak", 
23868         "roo-password-meter-medium", 
23869         "roo-password-meter-strong", 
23870         "roo-password-meter-grey"
23871     ];
23872     
23873     this.errors = {};
23874     
23875     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23876 }
23877
23878 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23879     /**
23880      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23881      * {
23882      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23883      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23884      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23885      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23886      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23887      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23888      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23889      * })
23890      */
23891     // private
23892     
23893     meterWidth: 300,
23894     errorMsg :'',    
23895     errors: false,
23896     imageRoot: '/',
23897     /**
23898      * @cfg {String/Object} Label for the strength meter (defaults to
23899      * 'Password strength:')
23900      */
23901     // private
23902     meterLabel: '',
23903     /**
23904      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23905      * ['Weak', 'Medium', 'Strong'])
23906      */
23907     // private    
23908     pwdStrengths: false,    
23909     // private
23910     strength: 0,
23911     // private
23912     _lastPwd: null,
23913     // private
23914     kCapitalLetter: 0,
23915     kSmallLetter: 1,
23916     kDigit: 2,
23917     kPunctuation: 3,
23918     
23919     insecure: false,
23920     // private
23921     initEvents: function ()
23922     {
23923         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23924
23925         if (this.el.is('input[type=password]') && Roo.isSafari) {
23926             this.el.on('keydown', this.SafariOnKeyDown, this);
23927         }
23928
23929         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23930     },
23931     // private
23932     onRender: function (ct, position)
23933     {
23934         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23935         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23936         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23937
23938         this.trigger.createChild({
23939                    cn: [
23940                     {
23941                     //id: 'PwdMeter',
23942                     tag: 'div',
23943                     cls: 'roo-password-meter-grey col-xs-12',
23944                     style: {
23945                         //width: 0,
23946                         //width: this.meterWidth + 'px'                                                
23947                         }
23948                     },
23949                     {                            
23950                          cls: 'roo-password-meter-text'                          
23951                     }
23952                 ]            
23953         });
23954
23955          
23956         if (this.hideTrigger) {
23957             this.trigger.setDisplayed(false);
23958         }
23959         this.setSize(this.width || '', this.height || '');
23960     },
23961     // private
23962     onDestroy: function ()
23963     {
23964         if (this.trigger) {
23965             this.trigger.removeAllListeners();
23966             this.trigger.remove();
23967         }
23968         if (this.wrap) {
23969             this.wrap.remove();
23970         }
23971         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23972     },
23973     // private
23974     checkStrength: function ()
23975     {
23976         var pwd = this.inputEl().getValue();
23977         if (pwd == this._lastPwd) {
23978             return;
23979         }
23980
23981         var strength;
23982         if (this.ClientSideStrongPassword(pwd)) {
23983             strength = 3;
23984         } else if (this.ClientSideMediumPassword(pwd)) {
23985             strength = 2;
23986         } else if (this.ClientSideWeakPassword(pwd)) {
23987             strength = 1;
23988         } else {
23989             strength = 0;
23990         }
23991         
23992         Roo.log('strength1: ' + strength);
23993         
23994         //var pm = this.trigger.child('div/div/div').dom;
23995         var pm = this.trigger.child('div/div');
23996         pm.removeClass(this.meterClass);
23997         pm.addClass(this.meterClass[strength]);
23998                 
23999         
24000         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24001                 
24002         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24003         
24004         this._lastPwd = pwd;
24005     },
24006     reset: function ()
24007     {
24008         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24009         
24010         this._lastPwd = '';
24011         
24012         var pm = this.trigger.child('div/div');
24013         pm.removeClass(this.meterClass);
24014         pm.addClass('roo-password-meter-grey');        
24015         
24016         
24017         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24018         
24019         pt.innerHTML = '';
24020         this.inputEl().dom.type='password';
24021     },
24022     // private
24023     validateValue: function (value)
24024     {
24025         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24026             return false;
24027         }
24028         if (value.length == 0) {
24029             if (this.allowBlank) {
24030                 this.clearInvalid();
24031                 return true;
24032             }
24033
24034             this.markInvalid(this.errors.PwdEmpty);
24035             this.errorMsg = this.errors.PwdEmpty;
24036             return false;
24037         }
24038         
24039         if(this.insecure){
24040             return true;
24041         }
24042         
24043         if (!value.match(/[\x21-\x7e]+/)) {
24044             this.markInvalid(this.errors.PwdBadChar);
24045             this.errorMsg = this.errors.PwdBadChar;
24046             return false;
24047         }
24048         if (value.length < 6) {
24049             this.markInvalid(this.errors.PwdShort);
24050             this.errorMsg = this.errors.PwdShort;
24051             return false;
24052         }
24053         if (value.length > 16) {
24054             this.markInvalid(this.errors.PwdLong);
24055             this.errorMsg = this.errors.PwdLong;
24056             return false;
24057         }
24058         var strength;
24059         if (this.ClientSideStrongPassword(value)) {
24060             strength = 3;
24061         } else if (this.ClientSideMediumPassword(value)) {
24062             strength = 2;
24063         } else if (this.ClientSideWeakPassword(value)) {
24064             strength = 1;
24065         } else {
24066             strength = 0;
24067         }
24068
24069         
24070         if (strength < 2) {
24071             //this.markInvalid(this.errors.TooWeak);
24072             this.errorMsg = this.errors.TooWeak;
24073             //return false;
24074         }
24075         
24076         
24077         console.log('strength2: ' + strength);
24078         
24079         //var pm = this.trigger.child('div/div/div').dom;
24080         
24081         var pm = this.trigger.child('div/div');
24082         pm.removeClass(this.meterClass);
24083         pm.addClass(this.meterClass[strength]);
24084                 
24085         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24086                 
24087         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24088         
24089         this.errorMsg = ''; 
24090         return true;
24091     },
24092     // private
24093     CharacterSetChecks: function (type)
24094     {
24095         this.type = type;
24096         this.fResult = false;
24097     },
24098     // private
24099     isctype: function (character, type)
24100     {
24101         switch (type) {  
24102             case this.kCapitalLetter:
24103                 if (character >= 'A' && character <= 'Z') {
24104                     return true;
24105                 }
24106                 break;
24107             
24108             case this.kSmallLetter:
24109                 if (character >= 'a' && character <= 'z') {
24110                     return true;
24111                 }
24112                 break;
24113             
24114             case this.kDigit:
24115                 if (character >= '0' && character <= '9') {
24116                     return true;
24117                 }
24118                 break;
24119             
24120             case this.kPunctuation:
24121                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24122                     return true;
24123                 }
24124                 break;
24125             
24126             default:
24127                 return false;
24128         }
24129
24130     },
24131     // private
24132     IsLongEnough: function (pwd, size)
24133     {
24134         return !(pwd == null || isNaN(size) || pwd.length < size);
24135     },
24136     // private
24137     SpansEnoughCharacterSets: function (word, nb)
24138     {
24139         if (!this.IsLongEnough(word, nb))
24140         {
24141             return false;
24142         }
24143
24144         var characterSetChecks = new Array(
24145             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24146             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24147         );
24148         
24149         for (var index = 0; index < word.length; ++index) {
24150             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24151                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24152                     characterSetChecks[nCharSet].fResult = true;
24153                     break;
24154                 }
24155             }
24156         }
24157
24158         var nCharSets = 0;
24159         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24160             if (characterSetChecks[nCharSet].fResult) {
24161                 ++nCharSets;
24162             }
24163         }
24164
24165         if (nCharSets < nb) {
24166             return false;
24167         }
24168         return true;
24169     },
24170     // private
24171     ClientSideStrongPassword: function (pwd)
24172     {
24173         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24174     },
24175     // private
24176     ClientSideMediumPassword: function (pwd)
24177     {
24178         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24179     },
24180     // private
24181     ClientSideWeakPassword: function (pwd)
24182     {
24183         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24184     }
24185           
24186 })//<script type="text/javascript">
24187
24188 /*
24189  * Based  Ext JS Library 1.1.1
24190  * Copyright(c) 2006-2007, Ext JS, LLC.
24191  * LGPL
24192  *
24193  */
24194  
24195 /**
24196  * @class Roo.HtmlEditorCore
24197  * @extends Roo.Component
24198  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24199  *
24200  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24201  */
24202
24203 Roo.HtmlEditorCore = function(config){
24204     
24205     
24206     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24207     
24208     
24209     this.addEvents({
24210         /**
24211          * @event initialize
24212          * Fires when the editor is fully initialized (including the iframe)
24213          * @param {Roo.HtmlEditorCore} this
24214          */
24215         initialize: true,
24216         /**
24217          * @event activate
24218          * Fires when the editor is first receives the focus. Any insertion must wait
24219          * until after this event.
24220          * @param {Roo.HtmlEditorCore} this
24221          */
24222         activate: true,
24223          /**
24224          * @event beforesync
24225          * Fires before the textarea is updated with content from the editor iframe. Return false
24226          * to cancel the sync.
24227          * @param {Roo.HtmlEditorCore} this
24228          * @param {String} html
24229          */
24230         beforesync: true,
24231          /**
24232          * @event beforepush
24233          * Fires before the iframe editor is updated with content from the textarea. Return false
24234          * to cancel the push.
24235          * @param {Roo.HtmlEditorCore} this
24236          * @param {String} html
24237          */
24238         beforepush: true,
24239          /**
24240          * @event sync
24241          * Fires when the textarea is updated with content from the editor iframe.
24242          * @param {Roo.HtmlEditorCore} this
24243          * @param {String} html
24244          */
24245         sync: true,
24246          /**
24247          * @event push
24248          * Fires when the iframe editor is updated with content from the textarea.
24249          * @param {Roo.HtmlEditorCore} this
24250          * @param {String} html
24251          */
24252         push: true,
24253         
24254         /**
24255          * @event editorevent
24256          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24257          * @param {Roo.HtmlEditorCore} this
24258          */
24259         editorevent: true
24260         
24261     });
24262     
24263     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24264     
24265     // defaults : white / black...
24266     this.applyBlacklists();
24267     
24268     
24269     
24270 };
24271
24272
24273 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24274
24275
24276      /**
24277      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24278      */
24279     
24280     owner : false,
24281     
24282      /**
24283      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24284      *                        Roo.resizable.
24285      */
24286     resizable : false,
24287      /**
24288      * @cfg {Number} height (in pixels)
24289      */   
24290     height: 300,
24291    /**
24292      * @cfg {Number} width (in pixels)
24293      */   
24294     width: 500,
24295     
24296     /**
24297      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24298      * 
24299      */
24300     stylesheets: false,
24301     
24302     // id of frame..
24303     frameId: false,
24304     
24305     // private properties
24306     validationEvent : false,
24307     deferHeight: true,
24308     initialized : false,
24309     activated : false,
24310     sourceEditMode : false,
24311     onFocus : Roo.emptyFn,
24312     iframePad:3,
24313     hideMode:'offsets',
24314     
24315     clearUp: true,
24316     
24317     // blacklist + whitelisted elements..
24318     black: false,
24319     white: false,
24320      
24321     bodyCls : '',
24322
24323     /**
24324      * Protected method that will not generally be called directly. It
24325      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24326      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24327      */
24328     getDocMarkup : function(){
24329         // body styles..
24330         var st = '';
24331         
24332         // inherit styels from page...?? 
24333         if (this.stylesheets === false) {
24334             
24335             Roo.get(document.head).select('style').each(function(node) {
24336                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24337             });
24338             
24339             Roo.get(document.head).select('link').each(function(node) { 
24340                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24341             });
24342             
24343         } else if (!this.stylesheets.length) {
24344                 // simple..
24345                 st = '<style type="text/css">' +
24346                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24347                    '</style>';
24348         } else {
24349             for (var i in this.stylesheets) { 
24350                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24351             }
24352             
24353         }
24354         
24355         st +=  '<style type="text/css">' +
24356             'IMG { cursor: pointer } ' +
24357         '</style>';
24358
24359         var cls = 'roo-htmleditor-body';
24360         
24361         if(this.bodyCls.length){
24362             cls += ' ' + this.bodyCls;
24363         }
24364         
24365         return '<html><head>' + st  +
24366             //<style type="text/css">' +
24367             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24368             //'</style>' +
24369             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24370     },
24371
24372     // private
24373     onRender : function(ct, position)
24374     {
24375         var _t = this;
24376         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24377         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24378         
24379         
24380         this.el.dom.style.border = '0 none';
24381         this.el.dom.setAttribute('tabIndex', -1);
24382         this.el.addClass('x-hidden hide');
24383         
24384         
24385         
24386         if(Roo.isIE){ // fix IE 1px bogus margin
24387             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24388         }
24389        
24390         
24391         this.frameId = Roo.id();
24392         
24393          
24394         
24395         var iframe = this.owner.wrap.createChild({
24396             tag: 'iframe',
24397             cls: 'form-control', // bootstrap..
24398             id: this.frameId,
24399             name: this.frameId,
24400             frameBorder : 'no',
24401             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24402         }, this.el
24403         );
24404         
24405         
24406         this.iframe = iframe.dom;
24407
24408          this.assignDocWin();
24409         
24410         this.doc.designMode = 'on';
24411        
24412         this.doc.open();
24413         this.doc.write(this.getDocMarkup());
24414         this.doc.close();
24415
24416         
24417         var task = { // must defer to wait for browser to be ready
24418             run : function(){
24419                 //console.log("run task?" + this.doc.readyState);
24420                 this.assignDocWin();
24421                 if(this.doc.body || this.doc.readyState == 'complete'){
24422                     try {
24423                         this.doc.designMode="on";
24424                     } catch (e) {
24425                         return;
24426                     }
24427                     Roo.TaskMgr.stop(task);
24428                     this.initEditor.defer(10, this);
24429                 }
24430             },
24431             interval : 10,
24432             duration: 10000,
24433             scope: this
24434         };
24435         Roo.TaskMgr.start(task);
24436
24437     },
24438
24439     // private
24440     onResize : function(w, h)
24441     {
24442          Roo.log('resize: ' +w + ',' + h );
24443         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24444         if(!this.iframe){
24445             return;
24446         }
24447         if(typeof w == 'number'){
24448             
24449             this.iframe.style.width = w + 'px';
24450         }
24451         if(typeof h == 'number'){
24452             
24453             this.iframe.style.height = h + 'px';
24454             if(this.doc){
24455                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24456             }
24457         }
24458         
24459     },
24460
24461     /**
24462      * Toggles the editor between standard and source edit mode.
24463      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24464      */
24465     toggleSourceEdit : function(sourceEditMode){
24466         
24467         this.sourceEditMode = sourceEditMode === true;
24468         
24469         if(this.sourceEditMode){
24470  
24471             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24472             
24473         }else{
24474             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24475             //this.iframe.className = '';
24476             this.deferFocus();
24477         }
24478         //this.setSize(this.owner.wrap.getSize());
24479         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24480     },
24481
24482     
24483   
24484
24485     /**
24486      * Protected method that will not generally be called directly. If you need/want
24487      * custom HTML cleanup, this is the method you should override.
24488      * @param {String} html The HTML to be cleaned
24489      * return {String} The cleaned HTML
24490      */
24491     cleanHtml : function(html){
24492         html = String(html);
24493         if(html.length > 5){
24494             if(Roo.isSafari){ // strip safari nonsense
24495                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24496             }
24497         }
24498         if(html == '&nbsp;'){
24499             html = '';
24500         }
24501         return html;
24502     },
24503
24504     /**
24505      * HTML Editor -> Textarea
24506      * Protected method that will not generally be called directly. Syncs the contents
24507      * of the editor iframe with the textarea.
24508      */
24509     syncValue : function(){
24510         if(this.initialized){
24511             var bd = (this.doc.body || this.doc.documentElement);
24512             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24513             var html = bd.innerHTML;
24514             if(Roo.isSafari){
24515                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24516                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24517                 if(m && m[1]){
24518                     html = '<div style="'+m[0]+'">' + html + '</div>';
24519                 }
24520             }
24521             html = this.cleanHtml(html);
24522             // fix up the special chars.. normaly like back quotes in word...
24523             // however we do not want to do this with chinese..
24524             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24525                 
24526                 var cc = match.charCodeAt();
24527
24528                 // Get the character value, handling surrogate pairs
24529                 if (match.length == 2) {
24530                     // It's a surrogate pair, calculate the Unicode code point
24531                     var high = match.charCodeAt(0) - 0xD800;
24532                     var low  = match.charCodeAt(1) - 0xDC00;
24533                     cc = (high * 0x400) + low + 0x10000;
24534                 }  else if (
24535                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24536                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24537                     (cc >= 0xf900 && cc < 0xfb00 )
24538                 ) {
24539                         return match;
24540                 }  
24541          
24542                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24543                 return "&#" + cc + ";";
24544                 
24545                 
24546             });
24547             
24548             
24549              
24550             if(this.owner.fireEvent('beforesync', this, html) !== false){
24551                 this.el.dom.value = html;
24552                 this.owner.fireEvent('sync', this, html);
24553             }
24554         }
24555     },
24556
24557     /**
24558      * Protected method that will not generally be called directly. Pushes the value of the textarea
24559      * into the iframe editor.
24560      */
24561     pushValue : function(){
24562         if(this.initialized){
24563             var v = this.el.dom.value.trim();
24564             
24565 //            if(v.length < 1){
24566 //                v = '&#160;';
24567 //            }
24568             
24569             if(this.owner.fireEvent('beforepush', this, v) !== false){
24570                 var d = (this.doc.body || this.doc.documentElement);
24571                 d.innerHTML = v;
24572                 this.cleanUpPaste();
24573                 this.el.dom.value = d.innerHTML;
24574                 this.owner.fireEvent('push', this, v);
24575             }
24576         }
24577     },
24578
24579     // private
24580     deferFocus : function(){
24581         this.focus.defer(10, this);
24582     },
24583
24584     // doc'ed in Field
24585     focus : function(){
24586         if(this.win && !this.sourceEditMode){
24587             this.win.focus();
24588         }else{
24589             this.el.focus();
24590         }
24591     },
24592     
24593     assignDocWin: function()
24594     {
24595         var iframe = this.iframe;
24596         
24597          if(Roo.isIE){
24598             this.doc = iframe.contentWindow.document;
24599             this.win = iframe.contentWindow;
24600         } else {
24601 //            if (!Roo.get(this.frameId)) {
24602 //                return;
24603 //            }
24604 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24605 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24606             
24607             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24608                 return;
24609             }
24610             
24611             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24612             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24613         }
24614     },
24615     
24616     // private
24617     initEditor : function(){
24618         //console.log("INIT EDITOR");
24619         this.assignDocWin();
24620         
24621         
24622         
24623         this.doc.designMode="on";
24624         this.doc.open();
24625         this.doc.write(this.getDocMarkup());
24626         this.doc.close();
24627         
24628         var dbody = (this.doc.body || this.doc.documentElement);
24629         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24630         // this copies styles from the containing element into thsi one..
24631         // not sure why we need all of this..
24632         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24633         
24634         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24635         //ss['background-attachment'] = 'fixed'; // w3c
24636         dbody.bgProperties = 'fixed'; // ie
24637         //Roo.DomHelper.applyStyles(dbody, ss);
24638         Roo.EventManager.on(this.doc, {
24639             //'mousedown': this.onEditorEvent,
24640             'mouseup': this.onEditorEvent,
24641             'dblclick': this.onEditorEvent,
24642             'click': this.onEditorEvent,
24643             'keyup': this.onEditorEvent,
24644             buffer:100,
24645             scope: this
24646         });
24647         if(Roo.isGecko){
24648             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24649         }
24650         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24651             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24652         }
24653         this.initialized = true;
24654
24655         this.owner.fireEvent('initialize', this);
24656         this.pushValue();
24657     },
24658
24659     // private
24660     onDestroy : function(){
24661         
24662         
24663         
24664         if(this.rendered){
24665             
24666             //for (var i =0; i < this.toolbars.length;i++) {
24667             //    // fixme - ask toolbars for heights?
24668             //    this.toolbars[i].onDestroy();
24669            // }
24670             
24671             //this.wrap.dom.innerHTML = '';
24672             //this.wrap.remove();
24673         }
24674     },
24675
24676     // private
24677     onFirstFocus : function(){
24678         
24679         this.assignDocWin();
24680         
24681         
24682         this.activated = true;
24683          
24684     
24685         if(Roo.isGecko){ // prevent silly gecko errors
24686             this.win.focus();
24687             var s = this.win.getSelection();
24688             if(!s.focusNode || s.focusNode.nodeType != 3){
24689                 var r = s.getRangeAt(0);
24690                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24691                 r.collapse(true);
24692                 this.deferFocus();
24693             }
24694             try{
24695                 this.execCmd('useCSS', true);
24696                 this.execCmd('styleWithCSS', false);
24697             }catch(e){}
24698         }
24699         this.owner.fireEvent('activate', this);
24700     },
24701
24702     // private
24703     adjustFont: function(btn){
24704         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24705         //if(Roo.isSafari){ // safari
24706         //    adjust *= 2;
24707        // }
24708         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24709         if(Roo.isSafari){ // safari
24710             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24711             v =  (v < 10) ? 10 : v;
24712             v =  (v > 48) ? 48 : v;
24713             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24714             
24715         }
24716         
24717         
24718         v = Math.max(1, v+adjust);
24719         
24720         this.execCmd('FontSize', v  );
24721     },
24722
24723     onEditorEvent : function(e)
24724     {
24725         this.owner.fireEvent('editorevent', this, e);
24726       //  this.updateToolbar();
24727         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24728     },
24729
24730     insertTag : function(tg)
24731     {
24732         // could be a bit smarter... -> wrap the current selected tRoo..
24733         if (tg.toLowerCase() == 'span' ||
24734             tg.toLowerCase() == 'code' ||
24735             tg.toLowerCase() == 'sup' ||
24736             tg.toLowerCase() == 'sub' 
24737             ) {
24738             
24739             range = this.createRange(this.getSelection());
24740             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24741             wrappingNode.appendChild(range.extractContents());
24742             range.insertNode(wrappingNode);
24743
24744             return;
24745             
24746             
24747             
24748         }
24749         this.execCmd("formatblock",   tg);
24750         
24751     },
24752     
24753     insertText : function(txt)
24754     {
24755         
24756         
24757         var range = this.createRange();
24758         range.deleteContents();
24759                //alert(Sender.getAttribute('label'));
24760                
24761         range.insertNode(this.doc.createTextNode(txt));
24762     } ,
24763     
24764      
24765
24766     /**
24767      * Executes a Midas editor command on the editor document and performs necessary focus and
24768      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24769      * @param {String} cmd The Midas command
24770      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24771      */
24772     relayCmd : function(cmd, value){
24773         this.win.focus();
24774         this.execCmd(cmd, value);
24775         this.owner.fireEvent('editorevent', this);
24776         //this.updateToolbar();
24777         this.owner.deferFocus();
24778     },
24779
24780     /**
24781      * Executes a Midas editor command directly on the editor document.
24782      * For visual commands, you should use {@link #relayCmd} instead.
24783      * <b>This should only be called after the editor is initialized.</b>
24784      * @param {String} cmd The Midas command
24785      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24786      */
24787     execCmd : function(cmd, value){
24788         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24789         this.syncValue();
24790     },
24791  
24792  
24793    
24794     /**
24795      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24796      * to insert tRoo.
24797      * @param {String} text | dom node.. 
24798      */
24799     insertAtCursor : function(text)
24800     {
24801         
24802         if(!this.activated){
24803             return;
24804         }
24805         /*
24806         if(Roo.isIE){
24807             this.win.focus();
24808             var r = this.doc.selection.createRange();
24809             if(r){
24810                 r.collapse(true);
24811                 r.pasteHTML(text);
24812                 this.syncValue();
24813                 this.deferFocus();
24814             
24815             }
24816             return;
24817         }
24818         */
24819         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24820             this.win.focus();
24821             
24822             
24823             // from jquery ui (MIT licenced)
24824             var range, node;
24825             var win = this.win;
24826             
24827             if (win.getSelection && win.getSelection().getRangeAt) {
24828                 range = win.getSelection().getRangeAt(0);
24829                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24830                 range.insertNode(node);
24831             } else if (win.document.selection && win.document.selection.createRange) {
24832                 // no firefox support
24833                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24834                 win.document.selection.createRange().pasteHTML(txt);
24835             } else {
24836                 // no firefox support
24837                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24838                 this.execCmd('InsertHTML', txt);
24839             } 
24840             
24841             this.syncValue();
24842             
24843             this.deferFocus();
24844         }
24845     },
24846  // private
24847     mozKeyPress : function(e){
24848         if(e.ctrlKey){
24849             var c = e.getCharCode(), cmd;
24850           
24851             if(c > 0){
24852                 c = String.fromCharCode(c).toLowerCase();
24853                 switch(c){
24854                     case 'b':
24855                         cmd = 'bold';
24856                         break;
24857                     case 'i':
24858                         cmd = 'italic';
24859                         break;
24860                     
24861                     case 'u':
24862                         cmd = 'underline';
24863                         break;
24864                     
24865                     case 'v':
24866                         this.cleanUpPaste.defer(100, this);
24867                         return;
24868                         
24869                 }
24870                 if(cmd){
24871                     this.win.focus();
24872                     this.execCmd(cmd);
24873                     this.deferFocus();
24874                     e.preventDefault();
24875                 }
24876                 
24877             }
24878         }
24879     },
24880
24881     // private
24882     fixKeys : function(){ // load time branching for fastest keydown performance
24883         if(Roo.isIE){
24884             return function(e){
24885                 var k = e.getKey(), r;
24886                 if(k == e.TAB){
24887                     e.stopEvent();
24888                     r = this.doc.selection.createRange();
24889                     if(r){
24890                         r.collapse(true);
24891                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24892                         this.deferFocus();
24893                     }
24894                     return;
24895                 }
24896                 
24897                 if(k == e.ENTER){
24898                     r = this.doc.selection.createRange();
24899                     if(r){
24900                         var target = r.parentElement();
24901                         if(!target || target.tagName.toLowerCase() != 'li'){
24902                             e.stopEvent();
24903                             r.pasteHTML('<br />');
24904                             r.collapse(false);
24905                             r.select();
24906                         }
24907                     }
24908                 }
24909                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24910                     this.cleanUpPaste.defer(100, this);
24911                     return;
24912                 }
24913                 
24914                 
24915             };
24916         }else if(Roo.isOpera){
24917             return function(e){
24918                 var k = e.getKey();
24919                 if(k == e.TAB){
24920                     e.stopEvent();
24921                     this.win.focus();
24922                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24923                     this.deferFocus();
24924                 }
24925                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24926                     this.cleanUpPaste.defer(100, this);
24927                     return;
24928                 }
24929                 
24930             };
24931         }else if(Roo.isSafari){
24932             return function(e){
24933                 var k = e.getKey();
24934                 
24935                 if(k == e.TAB){
24936                     e.stopEvent();
24937                     this.execCmd('InsertText','\t');
24938                     this.deferFocus();
24939                     return;
24940                 }
24941                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24942                     this.cleanUpPaste.defer(100, this);
24943                     return;
24944                 }
24945                 
24946              };
24947         }
24948     }(),
24949     
24950     getAllAncestors: function()
24951     {
24952         var p = this.getSelectedNode();
24953         var a = [];
24954         if (!p) {
24955             a.push(p); // push blank onto stack..
24956             p = this.getParentElement();
24957         }
24958         
24959         
24960         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24961             a.push(p);
24962             p = p.parentNode;
24963         }
24964         a.push(this.doc.body);
24965         return a;
24966     },
24967     lastSel : false,
24968     lastSelNode : false,
24969     
24970     
24971     getSelection : function() 
24972     {
24973         this.assignDocWin();
24974         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24975     },
24976     
24977     getSelectedNode: function() 
24978     {
24979         // this may only work on Gecko!!!
24980         
24981         // should we cache this!!!!
24982         
24983         
24984         
24985          
24986         var range = this.createRange(this.getSelection()).cloneRange();
24987         
24988         if (Roo.isIE) {
24989             var parent = range.parentElement();
24990             while (true) {
24991                 var testRange = range.duplicate();
24992                 testRange.moveToElementText(parent);
24993                 if (testRange.inRange(range)) {
24994                     break;
24995                 }
24996                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24997                     break;
24998                 }
24999                 parent = parent.parentElement;
25000             }
25001             return parent;
25002         }
25003         
25004         // is ancestor a text element.
25005         var ac =  range.commonAncestorContainer;
25006         if (ac.nodeType == 3) {
25007             ac = ac.parentNode;
25008         }
25009         
25010         var ar = ac.childNodes;
25011          
25012         var nodes = [];
25013         var other_nodes = [];
25014         var has_other_nodes = false;
25015         for (var i=0;i<ar.length;i++) {
25016             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25017                 continue;
25018             }
25019             // fullly contained node.
25020             
25021             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25022                 nodes.push(ar[i]);
25023                 continue;
25024             }
25025             
25026             // probably selected..
25027             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25028                 other_nodes.push(ar[i]);
25029                 continue;
25030             }
25031             // outer..
25032             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25033                 continue;
25034             }
25035             
25036             
25037             has_other_nodes = true;
25038         }
25039         if (!nodes.length && other_nodes.length) {
25040             nodes= other_nodes;
25041         }
25042         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25043             return false;
25044         }
25045         
25046         return nodes[0];
25047     },
25048     createRange: function(sel)
25049     {
25050         // this has strange effects when using with 
25051         // top toolbar - not sure if it's a great idea.
25052         //this.editor.contentWindow.focus();
25053         if (typeof sel != "undefined") {
25054             try {
25055                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25056             } catch(e) {
25057                 return this.doc.createRange();
25058             }
25059         } else {
25060             return this.doc.createRange();
25061         }
25062     },
25063     getParentElement: function()
25064     {
25065         
25066         this.assignDocWin();
25067         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25068         
25069         var range = this.createRange(sel);
25070          
25071         try {
25072             var p = range.commonAncestorContainer;
25073             while (p.nodeType == 3) { // text node
25074                 p = p.parentNode;
25075             }
25076             return p;
25077         } catch (e) {
25078             return null;
25079         }
25080     
25081     },
25082     /***
25083      *
25084      * Range intersection.. the hard stuff...
25085      *  '-1' = before
25086      *  '0' = hits..
25087      *  '1' = after.
25088      *         [ -- selected range --- ]
25089      *   [fail]                        [fail]
25090      *
25091      *    basically..
25092      *      if end is before start or  hits it. fail.
25093      *      if start is after end or hits it fail.
25094      *
25095      *   if either hits (but other is outside. - then it's not 
25096      *   
25097      *    
25098      **/
25099     
25100     
25101     // @see http://www.thismuchiknow.co.uk/?p=64.
25102     rangeIntersectsNode : function(range, node)
25103     {
25104         var nodeRange = node.ownerDocument.createRange();
25105         try {
25106             nodeRange.selectNode(node);
25107         } catch (e) {
25108             nodeRange.selectNodeContents(node);
25109         }
25110     
25111         var rangeStartRange = range.cloneRange();
25112         rangeStartRange.collapse(true);
25113     
25114         var rangeEndRange = range.cloneRange();
25115         rangeEndRange.collapse(false);
25116     
25117         var nodeStartRange = nodeRange.cloneRange();
25118         nodeStartRange.collapse(true);
25119     
25120         var nodeEndRange = nodeRange.cloneRange();
25121         nodeEndRange.collapse(false);
25122     
25123         return rangeStartRange.compareBoundaryPoints(
25124                  Range.START_TO_START, nodeEndRange) == -1 &&
25125                rangeEndRange.compareBoundaryPoints(
25126                  Range.START_TO_START, nodeStartRange) == 1;
25127         
25128          
25129     },
25130     rangeCompareNode : function(range, node)
25131     {
25132         var nodeRange = node.ownerDocument.createRange();
25133         try {
25134             nodeRange.selectNode(node);
25135         } catch (e) {
25136             nodeRange.selectNodeContents(node);
25137         }
25138         
25139         
25140         range.collapse(true);
25141     
25142         nodeRange.collapse(true);
25143      
25144         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25145         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25146          
25147         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25148         
25149         var nodeIsBefore   =  ss == 1;
25150         var nodeIsAfter    = ee == -1;
25151         
25152         if (nodeIsBefore && nodeIsAfter) {
25153             return 0; // outer
25154         }
25155         if (!nodeIsBefore && nodeIsAfter) {
25156             return 1; //right trailed.
25157         }
25158         
25159         if (nodeIsBefore && !nodeIsAfter) {
25160             return 2;  // left trailed.
25161         }
25162         // fully contined.
25163         return 3;
25164     },
25165
25166     // private? - in a new class?
25167     cleanUpPaste :  function()
25168     {
25169         // cleans up the whole document..
25170         Roo.log('cleanuppaste');
25171         
25172         this.cleanUpChildren(this.doc.body);
25173         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25174         if (clean != this.doc.body.innerHTML) {
25175             this.doc.body.innerHTML = clean;
25176         }
25177         
25178     },
25179     
25180     cleanWordChars : function(input) {// change the chars to hex code
25181         var he = Roo.HtmlEditorCore;
25182         
25183         var output = input;
25184         Roo.each(he.swapCodes, function(sw) { 
25185             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25186             
25187             output = output.replace(swapper, sw[1]);
25188         });
25189         
25190         return output;
25191     },
25192     
25193     
25194     cleanUpChildren : function (n)
25195     {
25196         if (!n.childNodes.length) {
25197             return;
25198         }
25199         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25200            this.cleanUpChild(n.childNodes[i]);
25201         }
25202     },
25203     
25204     
25205         
25206     
25207     cleanUpChild : function (node)
25208     {
25209         var ed = this;
25210         //console.log(node);
25211         if (node.nodeName == "#text") {
25212             // clean up silly Windows -- stuff?
25213             return; 
25214         }
25215         if (node.nodeName == "#comment") {
25216             node.parentNode.removeChild(node);
25217             // clean up silly Windows -- stuff?
25218             return; 
25219         }
25220         var lcname = node.tagName.toLowerCase();
25221         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25222         // whitelist of tags..
25223         
25224         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25225             // remove node.
25226             node.parentNode.removeChild(node);
25227             return;
25228             
25229         }
25230         
25231         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25232         
25233         // spans with no attributes - just remove them..
25234         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25235             remove_keep_children = true;
25236         }
25237         
25238         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25239         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25240         
25241         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25242         //    remove_keep_children = true;
25243         //}
25244         
25245         if (remove_keep_children) {
25246             this.cleanUpChildren(node);
25247             // inserts everything just before this node...
25248             while (node.childNodes.length) {
25249                 var cn = node.childNodes[0];
25250                 node.removeChild(cn);
25251                 node.parentNode.insertBefore(cn, node);
25252             }
25253             node.parentNode.removeChild(node);
25254             return;
25255         }
25256         
25257         if (!node.attributes || !node.attributes.length) {
25258             
25259           
25260             
25261             
25262             this.cleanUpChildren(node);
25263             return;
25264         }
25265         
25266         function cleanAttr(n,v)
25267         {
25268             
25269             if (v.match(/^\./) || v.match(/^\//)) {
25270                 return;
25271             }
25272             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25273                 return;
25274             }
25275             if (v.match(/^#/)) {
25276                 return;
25277             }
25278             if (v.match(/^\{/)) { // allow template editing.
25279                 return;
25280             }
25281 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25282             node.removeAttribute(n);
25283             
25284         }
25285         
25286         var cwhite = this.cwhite;
25287         var cblack = this.cblack;
25288             
25289         function cleanStyle(n,v)
25290         {
25291             if (v.match(/expression/)) { //XSS?? should we even bother..
25292                 node.removeAttribute(n);
25293                 return;
25294             }
25295             
25296             var parts = v.split(/;/);
25297             var clean = [];
25298             
25299             Roo.each(parts, function(p) {
25300                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25301                 if (!p.length) {
25302                     return true;
25303                 }
25304                 var l = p.split(':').shift().replace(/\s+/g,'');
25305                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25306                 
25307                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25308 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25309                     //node.removeAttribute(n);
25310                     return true;
25311                 }
25312                 //Roo.log()
25313                 // only allow 'c whitelisted system attributes'
25314                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25315 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25316                     //node.removeAttribute(n);
25317                     return true;
25318                 }
25319                 
25320                 
25321                  
25322                 
25323                 clean.push(p);
25324                 return true;
25325             });
25326             if (clean.length) { 
25327                 node.setAttribute(n, clean.join(';'));
25328             } else {
25329                 node.removeAttribute(n);
25330             }
25331             
25332         }
25333         
25334         
25335         for (var i = node.attributes.length-1; i > -1 ; i--) {
25336             var a = node.attributes[i];
25337             //console.log(a);
25338             
25339             if (a.name.toLowerCase().substr(0,2)=='on')  {
25340                 node.removeAttribute(a.name);
25341                 continue;
25342             }
25343             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25344                 node.removeAttribute(a.name);
25345                 continue;
25346             }
25347             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25348                 cleanAttr(a.name,a.value); // fixme..
25349                 continue;
25350             }
25351             if (a.name == 'style') {
25352                 cleanStyle(a.name,a.value);
25353                 continue;
25354             }
25355             /// clean up MS crap..
25356             // tecnically this should be a list of valid class'es..
25357             
25358             
25359             if (a.name == 'class') {
25360                 if (a.value.match(/^Mso/)) {
25361                     node.removeAttribute('class');
25362                 }
25363                 
25364                 if (a.value.match(/^body$/)) {
25365                     node.removeAttribute('class');
25366                 }
25367                 continue;
25368             }
25369             
25370             // style cleanup!?
25371             // class cleanup?
25372             
25373         }
25374         
25375         
25376         this.cleanUpChildren(node);
25377         
25378         
25379     },
25380     
25381     /**
25382      * Clean up MS wordisms...
25383      */
25384     cleanWord : function(node)
25385     {
25386         if (!node) {
25387             this.cleanWord(this.doc.body);
25388             return;
25389         }
25390         
25391         if(
25392                 node.nodeName == 'SPAN' &&
25393                 !node.hasAttributes() &&
25394                 node.childNodes.length == 1 &&
25395                 node.firstChild.nodeName == "#text"  
25396         ) {
25397             var textNode = node.firstChild;
25398             node.removeChild(textNode);
25399             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25400                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25401             }
25402             node.parentNode.insertBefore(textNode, node);
25403             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25404                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25405             }
25406             node.parentNode.removeChild(node);
25407         }
25408         
25409         if (node.nodeName == "#text") {
25410             // clean up silly Windows -- stuff?
25411             return; 
25412         }
25413         if (node.nodeName == "#comment") {
25414             node.parentNode.removeChild(node);
25415             // clean up silly Windows -- stuff?
25416             return; 
25417         }
25418         
25419         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25420             node.parentNode.removeChild(node);
25421             return;
25422         }
25423         //Roo.log(node.tagName);
25424         // remove - but keep children..
25425         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25426             //Roo.log('-- removed');
25427             while (node.childNodes.length) {
25428                 var cn = node.childNodes[0];
25429                 node.removeChild(cn);
25430                 node.parentNode.insertBefore(cn, node);
25431                 // move node to parent - and clean it..
25432                 this.cleanWord(cn);
25433             }
25434             node.parentNode.removeChild(node);
25435             /// no need to iterate chidlren = it's got none..
25436             //this.iterateChildren(node, this.cleanWord);
25437             return;
25438         }
25439         // clean styles
25440         if (node.className.length) {
25441             
25442             var cn = node.className.split(/\W+/);
25443             var cna = [];
25444             Roo.each(cn, function(cls) {
25445                 if (cls.match(/Mso[a-zA-Z]+/)) {
25446                     return;
25447                 }
25448                 cna.push(cls);
25449             });
25450             node.className = cna.length ? cna.join(' ') : '';
25451             if (!cna.length) {
25452                 node.removeAttribute("class");
25453             }
25454         }
25455         
25456         if (node.hasAttribute("lang")) {
25457             node.removeAttribute("lang");
25458         }
25459         
25460         if (node.hasAttribute("style")) {
25461             
25462             var styles = node.getAttribute("style").split(";");
25463             var nstyle = [];
25464             Roo.each(styles, function(s) {
25465                 if (!s.match(/:/)) {
25466                     return;
25467                 }
25468                 var kv = s.split(":");
25469                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25470                     return;
25471                 }
25472                 // what ever is left... we allow.
25473                 nstyle.push(s);
25474             });
25475             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25476             if (!nstyle.length) {
25477                 node.removeAttribute('style');
25478             }
25479         }
25480         this.iterateChildren(node, this.cleanWord);
25481         
25482         
25483         
25484     },
25485     /**
25486      * iterateChildren of a Node, calling fn each time, using this as the scole..
25487      * @param {DomNode} node node to iterate children of.
25488      * @param {Function} fn method of this class to call on each item.
25489      */
25490     iterateChildren : function(node, fn)
25491     {
25492         if (!node.childNodes.length) {
25493                 return;
25494         }
25495         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25496            fn.call(this, node.childNodes[i])
25497         }
25498     },
25499     
25500     
25501     /**
25502      * cleanTableWidths.
25503      *
25504      * Quite often pasting from word etc.. results in tables with column and widths.
25505      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25506      *
25507      */
25508     cleanTableWidths : function(node)
25509     {
25510          
25511          
25512         if (!node) {
25513             this.cleanTableWidths(this.doc.body);
25514             return;
25515         }
25516         
25517         // ignore list...
25518         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25519             return; 
25520         }
25521         Roo.log(node.tagName);
25522         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25523             this.iterateChildren(node, this.cleanTableWidths);
25524             return;
25525         }
25526         if (node.hasAttribute('width')) {
25527             node.removeAttribute('width');
25528         }
25529         
25530          
25531         if (node.hasAttribute("style")) {
25532             // pretty basic...
25533             
25534             var styles = node.getAttribute("style").split(";");
25535             var nstyle = [];
25536             Roo.each(styles, function(s) {
25537                 if (!s.match(/:/)) {
25538                     return;
25539                 }
25540                 var kv = s.split(":");
25541                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25542                     return;
25543                 }
25544                 // what ever is left... we allow.
25545                 nstyle.push(s);
25546             });
25547             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25548             if (!nstyle.length) {
25549                 node.removeAttribute('style');
25550             }
25551         }
25552         
25553         this.iterateChildren(node, this.cleanTableWidths);
25554         
25555         
25556     },
25557     
25558     
25559     
25560     
25561     domToHTML : function(currentElement, depth, nopadtext) {
25562         
25563         depth = depth || 0;
25564         nopadtext = nopadtext || false;
25565     
25566         if (!currentElement) {
25567             return this.domToHTML(this.doc.body);
25568         }
25569         
25570         //Roo.log(currentElement);
25571         var j;
25572         var allText = false;
25573         var nodeName = currentElement.nodeName;
25574         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25575         
25576         if  (nodeName == '#text') {
25577             
25578             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25579         }
25580         
25581         
25582         var ret = '';
25583         if (nodeName != 'BODY') {
25584              
25585             var i = 0;
25586             // Prints the node tagName, such as <A>, <IMG>, etc
25587             if (tagName) {
25588                 var attr = [];
25589                 for(i = 0; i < currentElement.attributes.length;i++) {
25590                     // quoting?
25591                     var aname = currentElement.attributes.item(i).name;
25592                     if (!currentElement.attributes.item(i).value.length) {
25593                         continue;
25594                     }
25595                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25596                 }
25597                 
25598                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25599             } 
25600             else {
25601                 
25602                 // eack
25603             }
25604         } else {
25605             tagName = false;
25606         }
25607         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25608             return ret;
25609         }
25610         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25611             nopadtext = true;
25612         }
25613         
25614         
25615         // Traverse the tree
25616         i = 0;
25617         var currentElementChild = currentElement.childNodes.item(i);
25618         var allText = true;
25619         var innerHTML  = '';
25620         lastnode = '';
25621         while (currentElementChild) {
25622             // Formatting code (indent the tree so it looks nice on the screen)
25623             var nopad = nopadtext;
25624             if (lastnode == 'SPAN') {
25625                 nopad  = true;
25626             }
25627             // text
25628             if  (currentElementChild.nodeName == '#text') {
25629                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25630                 toadd = nopadtext ? toadd : toadd.trim();
25631                 if (!nopad && toadd.length > 80) {
25632                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25633                 }
25634                 innerHTML  += toadd;
25635                 
25636                 i++;
25637                 currentElementChild = currentElement.childNodes.item(i);
25638                 lastNode = '';
25639                 continue;
25640             }
25641             allText = false;
25642             
25643             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25644                 
25645             // Recursively traverse the tree structure of the child node
25646             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25647             lastnode = currentElementChild.nodeName;
25648             i++;
25649             currentElementChild=currentElement.childNodes.item(i);
25650         }
25651         
25652         ret += innerHTML;
25653         
25654         if (!allText) {
25655                 // The remaining code is mostly for formatting the tree
25656             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25657         }
25658         
25659         
25660         if (tagName) {
25661             ret+= "</"+tagName+">";
25662         }
25663         return ret;
25664         
25665     },
25666         
25667     applyBlacklists : function()
25668     {
25669         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25670         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25671         
25672         this.white = [];
25673         this.black = [];
25674         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25675             if (b.indexOf(tag) > -1) {
25676                 return;
25677             }
25678             this.white.push(tag);
25679             
25680         }, this);
25681         
25682         Roo.each(w, function(tag) {
25683             if (b.indexOf(tag) > -1) {
25684                 return;
25685             }
25686             if (this.white.indexOf(tag) > -1) {
25687                 return;
25688             }
25689             this.white.push(tag);
25690             
25691         }, this);
25692         
25693         
25694         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25695             if (w.indexOf(tag) > -1) {
25696                 return;
25697             }
25698             this.black.push(tag);
25699             
25700         }, this);
25701         
25702         Roo.each(b, function(tag) {
25703             if (w.indexOf(tag) > -1) {
25704                 return;
25705             }
25706             if (this.black.indexOf(tag) > -1) {
25707                 return;
25708             }
25709             this.black.push(tag);
25710             
25711         }, this);
25712         
25713         
25714         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25715         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25716         
25717         this.cwhite = [];
25718         this.cblack = [];
25719         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25720             if (b.indexOf(tag) > -1) {
25721                 return;
25722             }
25723             this.cwhite.push(tag);
25724             
25725         }, this);
25726         
25727         Roo.each(w, function(tag) {
25728             if (b.indexOf(tag) > -1) {
25729                 return;
25730             }
25731             if (this.cwhite.indexOf(tag) > -1) {
25732                 return;
25733             }
25734             this.cwhite.push(tag);
25735             
25736         }, this);
25737         
25738         
25739         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25740             if (w.indexOf(tag) > -1) {
25741                 return;
25742             }
25743             this.cblack.push(tag);
25744             
25745         }, this);
25746         
25747         Roo.each(b, function(tag) {
25748             if (w.indexOf(tag) > -1) {
25749                 return;
25750             }
25751             if (this.cblack.indexOf(tag) > -1) {
25752                 return;
25753             }
25754             this.cblack.push(tag);
25755             
25756         }, this);
25757     },
25758     
25759     setStylesheets : function(stylesheets)
25760     {
25761         if(typeof(stylesheets) == 'string'){
25762             Roo.get(this.iframe.contentDocument.head).createChild({
25763                 tag : 'link',
25764                 rel : 'stylesheet',
25765                 type : 'text/css',
25766                 href : stylesheets
25767             });
25768             
25769             return;
25770         }
25771         var _this = this;
25772      
25773         Roo.each(stylesheets, function(s) {
25774             if(!s.length){
25775                 return;
25776             }
25777             
25778             Roo.get(_this.iframe.contentDocument.head).createChild({
25779                 tag : 'link',
25780                 rel : 'stylesheet',
25781                 type : 'text/css',
25782                 href : s
25783             });
25784         });
25785
25786         
25787     },
25788     
25789     removeStylesheets : function()
25790     {
25791         var _this = this;
25792         
25793         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25794             s.remove();
25795         });
25796     },
25797     
25798     setStyle : function(style)
25799     {
25800         Roo.get(this.iframe.contentDocument.head).createChild({
25801             tag : 'style',
25802             type : 'text/css',
25803             html : style
25804         });
25805
25806         return;
25807     }
25808     
25809     // hide stuff that is not compatible
25810     /**
25811      * @event blur
25812      * @hide
25813      */
25814     /**
25815      * @event change
25816      * @hide
25817      */
25818     /**
25819      * @event focus
25820      * @hide
25821      */
25822     /**
25823      * @event specialkey
25824      * @hide
25825      */
25826     /**
25827      * @cfg {String} fieldClass @hide
25828      */
25829     /**
25830      * @cfg {String} focusClass @hide
25831      */
25832     /**
25833      * @cfg {String} autoCreate @hide
25834      */
25835     /**
25836      * @cfg {String} inputType @hide
25837      */
25838     /**
25839      * @cfg {String} invalidClass @hide
25840      */
25841     /**
25842      * @cfg {String} invalidText @hide
25843      */
25844     /**
25845      * @cfg {String} msgFx @hide
25846      */
25847     /**
25848      * @cfg {String} validateOnBlur @hide
25849      */
25850 });
25851
25852 Roo.HtmlEditorCore.white = [
25853         'area', 'br', 'img', 'input', 'hr', 'wbr',
25854         
25855        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25856        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25857        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25858        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25859        'table',   'ul',         'xmp', 
25860        
25861        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25862       'thead',   'tr', 
25863      
25864       'dir', 'menu', 'ol', 'ul', 'dl',
25865        
25866       'embed',  'object'
25867 ];
25868
25869
25870 Roo.HtmlEditorCore.black = [
25871     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25872         'applet', // 
25873         'base',   'basefont', 'bgsound', 'blink',  'body', 
25874         'frame',  'frameset', 'head',    'html',   'ilayer', 
25875         'iframe', 'layer',  'link',     'meta',    'object',   
25876         'script', 'style' ,'title',  'xml' // clean later..
25877 ];
25878 Roo.HtmlEditorCore.clean = [
25879     'script', 'style', 'title', 'xml'
25880 ];
25881 Roo.HtmlEditorCore.remove = [
25882     'font'
25883 ];
25884 // attributes..
25885
25886 Roo.HtmlEditorCore.ablack = [
25887     'on'
25888 ];
25889     
25890 Roo.HtmlEditorCore.aclean = [ 
25891     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25892 ];
25893
25894 // protocols..
25895 Roo.HtmlEditorCore.pwhite= [
25896         'http',  'https',  'mailto'
25897 ];
25898
25899 // white listed style attributes.
25900 Roo.HtmlEditorCore.cwhite= [
25901       //  'text-align', /// default is to allow most things..
25902       
25903          
25904 //        'font-size'//??
25905 ];
25906
25907 // black listed style attributes.
25908 Roo.HtmlEditorCore.cblack= [
25909       //  'font-size' -- this can be set by the project 
25910 ];
25911
25912
25913 Roo.HtmlEditorCore.swapCodes   =[ 
25914     [    8211, "&#8211;" ], 
25915     [    8212, "&#8212;" ], 
25916     [    8216,  "'" ],  
25917     [    8217, "'" ],  
25918     [    8220, '"' ],  
25919     [    8221, '"' ],  
25920     [    8226, "*" ],  
25921     [    8230, "..." ]
25922 ]; 
25923
25924     /*
25925  * - LGPL
25926  *
25927  * HtmlEditor
25928  * 
25929  */
25930
25931 /**
25932  * @class Roo.bootstrap.HtmlEditor
25933  * @extends Roo.bootstrap.TextArea
25934  * Bootstrap HtmlEditor class
25935
25936  * @constructor
25937  * Create a new HtmlEditor
25938  * @param {Object} config The config object
25939  */
25940
25941 Roo.bootstrap.HtmlEditor = function(config){
25942     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25943     if (!this.toolbars) {
25944         this.toolbars = [];
25945     }
25946     
25947     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25948     this.addEvents({
25949             /**
25950              * @event initialize
25951              * Fires when the editor is fully initialized (including the iframe)
25952              * @param {HtmlEditor} this
25953              */
25954             initialize: true,
25955             /**
25956              * @event activate
25957              * Fires when the editor is first receives the focus. Any insertion must wait
25958              * until after this event.
25959              * @param {HtmlEditor} this
25960              */
25961             activate: true,
25962              /**
25963              * @event beforesync
25964              * Fires before the textarea is updated with content from the editor iframe. Return false
25965              * to cancel the sync.
25966              * @param {HtmlEditor} this
25967              * @param {String} html
25968              */
25969             beforesync: true,
25970              /**
25971              * @event beforepush
25972              * Fires before the iframe editor is updated with content from the textarea. Return false
25973              * to cancel the push.
25974              * @param {HtmlEditor} this
25975              * @param {String} html
25976              */
25977             beforepush: true,
25978              /**
25979              * @event sync
25980              * Fires when the textarea is updated with content from the editor iframe.
25981              * @param {HtmlEditor} this
25982              * @param {String} html
25983              */
25984             sync: true,
25985              /**
25986              * @event push
25987              * Fires when the iframe editor is updated with content from the textarea.
25988              * @param {HtmlEditor} this
25989              * @param {String} html
25990              */
25991             push: true,
25992              /**
25993              * @event editmodechange
25994              * Fires when the editor switches edit modes
25995              * @param {HtmlEditor} this
25996              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25997              */
25998             editmodechange: true,
25999             /**
26000              * @event editorevent
26001              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26002              * @param {HtmlEditor} this
26003              */
26004             editorevent: true,
26005             /**
26006              * @event firstfocus
26007              * Fires when on first focus - needed by toolbars..
26008              * @param {HtmlEditor} this
26009              */
26010             firstfocus: true,
26011             /**
26012              * @event autosave
26013              * Auto save the htmlEditor value as a file into Events
26014              * @param {HtmlEditor} this
26015              */
26016             autosave: true,
26017             /**
26018              * @event savedpreview
26019              * preview the saved version of htmlEditor
26020              * @param {HtmlEditor} this
26021              */
26022             savedpreview: true
26023         });
26024 };
26025
26026
26027 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26028     
26029     
26030       /**
26031      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26032      */
26033     toolbars : false,
26034     
26035      /**
26036     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26037     */
26038     btns : [],
26039    
26040      /**
26041      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26042      *                        Roo.resizable.
26043      */
26044     resizable : false,
26045      /**
26046      * @cfg {Number} height (in pixels)
26047      */   
26048     height: 300,
26049    /**
26050      * @cfg {Number} width (in pixels)
26051      */   
26052     width: false,
26053     
26054     /**
26055      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26056      * 
26057      */
26058     stylesheets: false,
26059     
26060     // id of frame..
26061     frameId: false,
26062     
26063     // private properties
26064     validationEvent : false,
26065     deferHeight: true,
26066     initialized : false,
26067     activated : false,
26068     
26069     onFocus : Roo.emptyFn,
26070     iframePad:3,
26071     hideMode:'offsets',
26072     
26073     tbContainer : false,
26074     
26075     bodyCls : '',
26076     
26077     toolbarContainer :function() {
26078         return this.wrap.select('.x-html-editor-tb',true).first();
26079     },
26080
26081     /**
26082      * Protected method that will not generally be called directly. It
26083      * is called when the editor creates its toolbar. Override this method if you need to
26084      * add custom toolbar buttons.
26085      * @param {HtmlEditor} editor
26086      */
26087     createToolbar : function(){
26088         Roo.log('renewing');
26089         Roo.log("create toolbars");
26090         
26091         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26092         this.toolbars[0].render(this.toolbarContainer());
26093         
26094         return;
26095         
26096 //        if (!editor.toolbars || !editor.toolbars.length) {
26097 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26098 //        }
26099 //        
26100 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26101 //            editor.toolbars[i] = Roo.factory(
26102 //                    typeof(editor.toolbars[i]) == 'string' ?
26103 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26104 //                Roo.bootstrap.HtmlEditor);
26105 //            editor.toolbars[i].init(editor);
26106 //        }
26107     },
26108
26109      
26110     // private
26111     onRender : function(ct, position)
26112     {
26113        // Roo.log("Call onRender: " + this.xtype);
26114         var _t = this;
26115         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26116       
26117         this.wrap = this.inputEl().wrap({
26118             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26119         });
26120         
26121         this.editorcore.onRender(ct, position);
26122          
26123         if (this.resizable) {
26124             this.resizeEl = new Roo.Resizable(this.wrap, {
26125                 pinned : true,
26126                 wrap: true,
26127                 dynamic : true,
26128                 minHeight : this.height,
26129                 height: this.height,
26130                 handles : this.resizable,
26131                 width: this.width,
26132                 listeners : {
26133                     resize : function(r, w, h) {
26134                         _t.onResize(w,h); // -something
26135                     }
26136                 }
26137             });
26138             
26139         }
26140         this.createToolbar(this);
26141        
26142         
26143         if(!this.width && this.resizable){
26144             this.setSize(this.wrap.getSize());
26145         }
26146         if (this.resizeEl) {
26147             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26148             // should trigger onReize..
26149         }
26150         
26151     },
26152
26153     // private
26154     onResize : function(w, h)
26155     {
26156         Roo.log('resize: ' +w + ',' + h );
26157         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26158         var ew = false;
26159         var eh = false;
26160         
26161         if(this.inputEl() ){
26162             if(typeof w == 'number'){
26163                 var aw = w - this.wrap.getFrameWidth('lr');
26164                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26165                 ew = aw;
26166             }
26167             if(typeof h == 'number'){
26168                  var tbh = -11;  // fixme it needs to tool bar size!
26169                 for (var i =0; i < this.toolbars.length;i++) {
26170                     // fixme - ask toolbars for heights?
26171                     tbh += this.toolbars[i].el.getHeight();
26172                     //if (this.toolbars[i].footer) {
26173                     //    tbh += this.toolbars[i].footer.el.getHeight();
26174                     //}
26175                 }
26176               
26177                 
26178                 
26179                 
26180                 
26181                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26182                 ah -= 5; // knock a few pixes off for look..
26183                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26184                 var eh = ah;
26185             }
26186         }
26187         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26188         this.editorcore.onResize(ew,eh);
26189         
26190     },
26191
26192     /**
26193      * Toggles the editor between standard and source edit mode.
26194      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26195      */
26196     toggleSourceEdit : function(sourceEditMode)
26197     {
26198         this.editorcore.toggleSourceEdit(sourceEditMode);
26199         
26200         if(this.editorcore.sourceEditMode){
26201             Roo.log('editor - showing textarea');
26202             
26203 //            Roo.log('in');
26204 //            Roo.log(this.syncValue());
26205             this.syncValue();
26206             this.inputEl().removeClass(['hide', 'x-hidden']);
26207             this.inputEl().dom.removeAttribute('tabIndex');
26208             this.inputEl().focus();
26209         }else{
26210             Roo.log('editor - hiding textarea');
26211 //            Roo.log('out')
26212 //            Roo.log(this.pushValue()); 
26213             this.pushValue();
26214             
26215             this.inputEl().addClass(['hide', 'x-hidden']);
26216             this.inputEl().dom.setAttribute('tabIndex', -1);
26217             //this.deferFocus();
26218         }
26219          
26220         if(this.resizable){
26221             this.setSize(this.wrap.getSize());
26222         }
26223         
26224         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26225     },
26226  
26227     // private (for BoxComponent)
26228     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26229
26230     // private (for BoxComponent)
26231     getResizeEl : function(){
26232         return this.wrap;
26233     },
26234
26235     // private (for BoxComponent)
26236     getPositionEl : function(){
26237         return this.wrap;
26238     },
26239
26240     // private
26241     initEvents : function(){
26242         this.originalValue = this.getValue();
26243     },
26244
26245 //    /**
26246 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26247 //     * @method
26248 //     */
26249 //    markInvalid : Roo.emptyFn,
26250 //    /**
26251 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26252 //     * @method
26253 //     */
26254 //    clearInvalid : Roo.emptyFn,
26255
26256     setValue : function(v){
26257         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26258         this.editorcore.pushValue();
26259     },
26260
26261      
26262     // private
26263     deferFocus : function(){
26264         this.focus.defer(10, this);
26265     },
26266
26267     // doc'ed in Field
26268     focus : function(){
26269         this.editorcore.focus();
26270         
26271     },
26272       
26273
26274     // private
26275     onDestroy : function(){
26276         
26277         
26278         
26279         if(this.rendered){
26280             
26281             for (var i =0; i < this.toolbars.length;i++) {
26282                 // fixme - ask toolbars for heights?
26283                 this.toolbars[i].onDestroy();
26284             }
26285             
26286             this.wrap.dom.innerHTML = '';
26287             this.wrap.remove();
26288         }
26289     },
26290
26291     // private
26292     onFirstFocus : function(){
26293         //Roo.log("onFirstFocus");
26294         this.editorcore.onFirstFocus();
26295          for (var i =0; i < this.toolbars.length;i++) {
26296             this.toolbars[i].onFirstFocus();
26297         }
26298         
26299     },
26300     
26301     // private
26302     syncValue : function()
26303     {   
26304         this.editorcore.syncValue();
26305     },
26306     
26307     pushValue : function()
26308     {   
26309         this.editorcore.pushValue();
26310     }
26311      
26312     
26313     // hide stuff that is not compatible
26314     /**
26315      * @event blur
26316      * @hide
26317      */
26318     /**
26319      * @event change
26320      * @hide
26321      */
26322     /**
26323      * @event focus
26324      * @hide
26325      */
26326     /**
26327      * @event specialkey
26328      * @hide
26329      */
26330     /**
26331      * @cfg {String} fieldClass @hide
26332      */
26333     /**
26334      * @cfg {String} focusClass @hide
26335      */
26336     /**
26337      * @cfg {String} autoCreate @hide
26338      */
26339     /**
26340      * @cfg {String} inputType @hide
26341      */
26342      
26343     /**
26344      * @cfg {String} invalidText @hide
26345      */
26346     /**
26347      * @cfg {String} msgFx @hide
26348      */
26349     /**
26350      * @cfg {String} validateOnBlur @hide
26351      */
26352 });
26353  
26354     
26355    
26356    
26357    
26358       
26359 Roo.namespace('Roo.bootstrap.htmleditor');
26360 /**
26361  * @class Roo.bootstrap.HtmlEditorToolbar1
26362  * Basic Toolbar
26363  * 
26364  * @example
26365  * Usage:
26366  *
26367  new Roo.bootstrap.HtmlEditor({
26368     ....
26369     toolbars : [
26370         new Roo.bootstrap.HtmlEditorToolbar1({
26371             disable : { fonts: 1 , format: 1, ..., ... , ...],
26372             btns : [ .... ]
26373         })
26374     }
26375      
26376  * 
26377  * @cfg {Object} disable List of elements to disable..
26378  * @cfg {Array} btns List of additional buttons.
26379  * 
26380  * 
26381  * NEEDS Extra CSS? 
26382  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26383  */
26384  
26385 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26386 {
26387     
26388     Roo.apply(this, config);
26389     
26390     // default disabled, based on 'good practice'..
26391     this.disable = this.disable || {};
26392     Roo.applyIf(this.disable, {
26393         fontSize : true,
26394         colors : true,
26395         specialElements : true
26396     });
26397     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26398     
26399     this.editor = config.editor;
26400     this.editorcore = config.editor.editorcore;
26401     
26402     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26403     
26404     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26405     // dont call parent... till later.
26406 }
26407 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26408      
26409     bar : true,
26410     
26411     editor : false,
26412     editorcore : false,
26413     
26414     
26415     formats : [
26416         "p" ,  
26417         "h1","h2","h3","h4","h5","h6", 
26418         "pre", "code", 
26419         "abbr", "acronym", "address", "cite", "samp", "var",
26420         'div','span'
26421     ],
26422     
26423     onRender : function(ct, position)
26424     {
26425        // Roo.log("Call onRender: " + this.xtype);
26426         
26427        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26428        Roo.log(this.el);
26429        this.el.dom.style.marginBottom = '0';
26430        var _this = this;
26431        var editorcore = this.editorcore;
26432        var editor= this.editor;
26433        
26434        var children = [];
26435        var btn = function(id,cmd , toggle, handler, html){
26436        
26437             var  event = toggle ? 'toggle' : 'click';
26438        
26439             var a = {
26440                 size : 'sm',
26441                 xtype: 'Button',
26442                 xns: Roo.bootstrap,
26443                 //glyphicon : id,
26444                 fa: id,
26445                 cmd : id || cmd,
26446                 enableToggle:toggle !== false,
26447                 html : html || '',
26448                 pressed : toggle ? false : null,
26449                 listeners : {}
26450             };
26451             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26452                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26453             };
26454             children.push(a);
26455             return a;
26456        }
26457        
26458     //    var cb_box = function...
26459         
26460         var style = {
26461                 xtype: 'Button',
26462                 size : 'sm',
26463                 xns: Roo.bootstrap,
26464                 fa : 'font',
26465                 //html : 'submit'
26466                 menu : {
26467                     xtype: 'Menu',
26468                     xns: Roo.bootstrap,
26469                     items:  []
26470                 }
26471         };
26472         Roo.each(this.formats, function(f) {
26473             style.menu.items.push({
26474                 xtype :'MenuItem',
26475                 xns: Roo.bootstrap,
26476                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26477                 tagname : f,
26478                 listeners : {
26479                     click : function()
26480                     {
26481                         editorcore.insertTag(this.tagname);
26482                         editor.focus();
26483                     }
26484                 }
26485                 
26486             });
26487         });
26488         children.push(style);   
26489         
26490         btn('bold',false,true);
26491         btn('italic',false,true);
26492         btn('align-left', 'justifyleft',true);
26493         btn('align-center', 'justifycenter',true);
26494         btn('align-right' , 'justifyright',true);
26495         btn('link', false, false, function(btn) {
26496             //Roo.log("create link?");
26497             var url = prompt(this.createLinkText, this.defaultLinkValue);
26498             if(url && url != 'http:/'+'/'){
26499                 this.editorcore.relayCmd('createlink', url);
26500             }
26501         }),
26502         btn('list','insertunorderedlist',true);
26503         btn('pencil', false,true, function(btn){
26504                 Roo.log(this);
26505                 this.toggleSourceEdit(btn.pressed);
26506         });
26507         
26508         if (this.editor.btns.length > 0) {
26509             for (var i = 0; i<this.editor.btns.length; i++) {
26510                 children.push(this.editor.btns[i]);
26511             }
26512         }
26513         
26514         /*
26515         var cog = {
26516                 xtype: 'Button',
26517                 size : 'sm',
26518                 xns: Roo.bootstrap,
26519                 glyphicon : 'cog',
26520                 //html : 'submit'
26521                 menu : {
26522                     xtype: 'Menu',
26523                     xns: Roo.bootstrap,
26524                     items:  []
26525                 }
26526         };
26527         
26528         cog.menu.items.push({
26529             xtype :'MenuItem',
26530             xns: Roo.bootstrap,
26531             html : Clean styles,
26532             tagname : f,
26533             listeners : {
26534                 click : function()
26535                 {
26536                     editorcore.insertTag(this.tagname);
26537                     editor.focus();
26538                 }
26539             }
26540             
26541         });
26542        */
26543         
26544          
26545        this.xtype = 'NavSimplebar';
26546         
26547         for(var i=0;i< children.length;i++) {
26548             
26549             this.buttons.add(this.addxtypeChild(children[i]));
26550             
26551         }
26552         
26553         editor.on('editorevent', this.updateToolbar, this);
26554     },
26555     onBtnClick : function(id)
26556     {
26557        this.editorcore.relayCmd(id);
26558        this.editorcore.focus();
26559     },
26560     
26561     /**
26562      * Protected method that will not generally be called directly. It triggers
26563      * a toolbar update by reading the markup state of the current selection in the editor.
26564      */
26565     updateToolbar: function(){
26566
26567         if(!this.editorcore.activated){
26568             this.editor.onFirstFocus(); // is this neeed?
26569             return;
26570         }
26571
26572         var btns = this.buttons; 
26573         var doc = this.editorcore.doc;
26574         btns.get('bold').setActive(doc.queryCommandState('bold'));
26575         btns.get('italic').setActive(doc.queryCommandState('italic'));
26576         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26577         
26578         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26579         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26580         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26581         
26582         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26583         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26584          /*
26585         
26586         var ans = this.editorcore.getAllAncestors();
26587         if (this.formatCombo) {
26588             
26589             
26590             var store = this.formatCombo.store;
26591             this.formatCombo.setValue("");
26592             for (var i =0; i < ans.length;i++) {
26593                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26594                     // select it..
26595                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26596                     break;
26597                 }
26598             }
26599         }
26600         
26601         
26602         
26603         // hides menus... - so this cant be on a menu...
26604         Roo.bootstrap.MenuMgr.hideAll();
26605         */
26606         Roo.bootstrap.MenuMgr.hideAll();
26607         //this.editorsyncValue();
26608     },
26609     onFirstFocus: function() {
26610         this.buttons.each(function(item){
26611            item.enable();
26612         });
26613     },
26614     toggleSourceEdit : function(sourceEditMode){
26615         
26616           
26617         if(sourceEditMode){
26618             Roo.log("disabling buttons");
26619            this.buttons.each( function(item){
26620                 if(item.cmd != 'pencil'){
26621                     item.disable();
26622                 }
26623             });
26624           
26625         }else{
26626             Roo.log("enabling buttons");
26627             if(this.editorcore.initialized){
26628                 this.buttons.each( function(item){
26629                     item.enable();
26630                 });
26631             }
26632             
26633         }
26634         Roo.log("calling toggole on editor");
26635         // tell the editor that it's been pressed..
26636         this.editor.toggleSourceEdit(sourceEditMode);
26637        
26638     }
26639 });
26640
26641
26642
26643
26644  
26645 /*
26646  * - LGPL
26647  */
26648
26649 /**
26650  * @class Roo.bootstrap.Markdown
26651  * @extends Roo.bootstrap.TextArea
26652  * Bootstrap Showdown editable area
26653  * @cfg {string} content
26654  * 
26655  * @constructor
26656  * Create a new Showdown
26657  */
26658
26659 Roo.bootstrap.Markdown = function(config){
26660     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26661    
26662 };
26663
26664 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26665     
26666     editing :false,
26667     
26668     initEvents : function()
26669     {
26670         
26671         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26672         this.markdownEl = this.el.createChild({
26673             cls : 'roo-markdown-area'
26674         });
26675         this.inputEl().addClass('d-none');
26676         if (this.getValue() == '') {
26677             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26678             
26679         } else {
26680             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26681         }
26682         this.markdownEl.on('click', this.toggleTextEdit, this);
26683         this.on('blur', this.toggleTextEdit, this);
26684         this.on('specialkey', this.resizeTextArea, this);
26685     },
26686     
26687     toggleTextEdit : function()
26688     {
26689         var sh = this.markdownEl.getHeight();
26690         this.inputEl().addClass('d-none');
26691         this.markdownEl.addClass('d-none');
26692         if (!this.editing) {
26693             // show editor?
26694             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26695             this.inputEl().removeClass('d-none');
26696             this.inputEl().focus();
26697             this.editing = true;
26698             return;
26699         }
26700         // show showdown...
26701         this.updateMarkdown();
26702         this.markdownEl.removeClass('d-none');
26703         this.editing = false;
26704         return;
26705     },
26706     updateMarkdown : function()
26707     {
26708         if (this.getValue() == '') {
26709             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26710             return;
26711         }
26712  
26713         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26714     },
26715     
26716     resizeTextArea: function () {
26717         
26718         var sh = 100;
26719         Roo.log([sh, this.getValue().split("\n").length * 30]);
26720         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26721     },
26722     setValue : function(val)
26723     {
26724         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26725         if (!this.editing) {
26726             this.updateMarkdown();
26727         }
26728         
26729     },
26730     focus : function()
26731     {
26732         if (!this.editing) {
26733             this.toggleTextEdit();
26734         }
26735         
26736     }
26737
26738
26739 });
26740 /**
26741  * @class Roo.bootstrap.Table.AbstractSelectionModel
26742  * @extends Roo.util.Observable
26743  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26744  * implemented by descendant classes.  This class should not be directly instantiated.
26745  * @constructor
26746  */
26747 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26748     this.locked = false;
26749     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26750 };
26751
26752
26753 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26754     /** @ignore Called by the grid automatically. Do not call directly. */
26755     init : function(grid){
26756         this.grid = grid;
26757         this.initEvents();
26758     },
26759
26760     /**
26761      * Locks the selections.
26762      */
26763     lock : function(){
26764         this.locked = true;
26765     },
26766
26767     /**
26768      * Unlocks the selections.
26769      */
26770     unlock : function(){
26771         this.locked = false;
26772     },
26773
26774     /**
26775      * Returns true if the selections are locked.
26776      * @return {Boolean}
26777      */
26778     isLocked : function(){
26779         return this.locked;
26780     },
26781     
26782     
26783     initEvents : function ()
26784     {
26785         
26786     }
26787 });
26788 /**
26789  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26790  * @class Roo.bootstrap.Table.RowSelectionModel
26791  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26792  * It supports multiple selections and keyboard selection/navigation. 
26793  * @constructor
26794  * @param {Object} config
26795  */
26796
26797 Roo.bootstrap.Table.RowSelectionModel = function(config){
26798     Roo.apply(this, config);
26799     this.selections = new Roo.util.MixedCollection(false, function(o){
26800         return o.id;
26801     });
26802
26803     this.last = false;
26804     this.lastActive = false;
26805
26806     this.addEvents({
26807         /**
26808              * @event selectionchange
26809              * Fires when the selection changes
26810              * @param {SelectionModel} this
26811              */
26812             "selectionchange" : true,
26813         /**
26814              * @event afterselectionchange
26815              * Fires after the selection changes (eg. by key press or clicking)
26816              * @param {SelectionModel} this
26817              */
26818             "afterselectionchange" : true,
26819         /**
26820              * @event beforerowselect
26821              * Fires when a row is selected being selected, return false to cancel.
26822              * @param {SelectionModel} this
26823              * @param {Number} rowIndex The selected index
26824              * @param {Boolean} keepExisting False if other selections will be cleared
26825              */
26826             "beforerowselect" : true,
26827         /**
26828              * @event rowselect
26829              * Fires when a row is selected.
26830              * @param {SelectionModel} this
26831              * @param {Number} rowIndex The selected index
26832              * @param {Roo.data.Record} r The record
26833              */
26834             "rowselect" : true,
26835         /**
26836              * @event rowdeselect
26837              * Fires when a row is deselected.
26838              * @param {SelectionModel} this
26839              * @param {Number} rowIndex The selected index
26840              */
26841         "rowdeselect" : true
26842     });
26843     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26844     this.locked = false;
26845  };
26846
26847 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26848     /**
26849      * @cfg {Boolean} singleSelect
26850      * True to allow selection of only one row at a time (defaults to false)
26851      */
26852     singleSelect : false,
26853
26854     // private
26855     initEvents : function()
26856     {
26857
26858         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26859         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26860         //}else{ // allow click to work like normal
26861          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26862         //}
26863         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26864         this.grid.on("rowclick", this.handleMouseDown, this);
26865         
26866         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26867             "up" : function(e){
26868                 if(!e.shiftKey){
26869                     this.selectPrevious(e.shiftKey);
26870                 }else if(this.last !== false && this.lastActive !== false){
26871                     var last = this.last;
26872                     this.selectRange(this.last,  this.lastActive-1);
26873                     this.grid.getView().focusRow(this.lastActive);
26874                     if(last !== false){
26875                         this.last = last;
26876                     }
26877                 }else{
26878                     this.selectFirstRow();
26879                 }
26880                 this.fireEvent("afterselectionchange", this);
26881             },
26882             "down" : function(e){
26883                 if(!e.shiftKey){
26884                     this.selectNext(e.shiftKey);
26885                 }else if(this.last !== false && this.lastActive !== false){
26886                     var last = this.last;
26887                     this.selectRange(this.last,  this.lastActive+1);
26888                     this.grid.getView().focusRow(this.lastActive);
26889                     if(last !== false){
26890                         this.last = last;
26891                     }
26892                 }else{
26893                     this.selectFirstRow();
26894                 }
26895                 this.fireEvent("afterselectionchange", this);
26896             },
26897             scope: this
26898         });
26899         this.grid.store.on('load', function(){
26900             this.selections.clear();
26901         },this);
26902         /*
26903         var view = this.grid.view;
26904         view.on("refresh", this.onRefresh, this);
26905         view.on("rowupdated", this.onRowUpdated, this);
26906         view.on("rowremoved", this.onRemove, this);
26907         */
26908     },
26909
26910     // private
26911     onRefresh : function()
26912     {
26913         var ds = this.grid.store, i, v = this.grid.view;
26914         var s = this.selections;
26915         s.each(function(r){
26916             if((i = ds.indexOfId(r.id)) != -1){
26917                 v.onRowSelect(i);
26918             }else{
26919                 s.remove(r);
26920             }
26921         });
26922     },
26923
26924     // private
26925     onRemove : function(v, index, r){
26926         this.selections.remove(r);
26927     },
26928
26929     // private
26930     onRowUpdated : function(v, index, r){
26931         if(this.isSelected(r)){
26932             v.onRowSelect(index);
26933         }
26934     },
26935
26936     /**
26937      * Select records.
26938      * @param {Array} records The records to select
26939      * @param {Boolean} keepExisting (optional) True to keep existing selections
26940      */
26941     selectRecords : function(records, keepExisting)
26942     {
26943         if(!keepExisting){
26944             this.clearSelections();
26945         }
26946             var ds = this.grid.store;
26947         for(var i = 0, len = records.length; i < len; i++){
26948             this.selectRow(ds.indexOf(records[i]), true);
26949         }
26950     },
26951
26952     /**
26953      * Gets the number of selected rows.
26954      * @return {Number}
26955      */
26956     getCount : function(){
26957         return this.selections.length;
26958     },
26959
26960     /**
26961      * Selects the first row in the grid.
26962      */
26963     selectFirstRow : function(){
26964         this.selectRow(0);
26965     },
26966
26967     /**
26968      * Select the last row.
26969      * @param {Boolean} keepExisting (optional) True to keep existing selections
26970      */
26971     selectLastRow : function(keepExisting){
26972         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26973         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26974     },
26975
26976     /**
26977      * Selects the row immediately following the last selected row.
26978      * @param {Boolean} keepExisting (optional) True to keep existing selections
26979      */
26980     selectNext : function(keepExisting)
26981     {
26982             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26983             this.selectRow(this.last+1, keepExisting);
26984             this.grid.getView().focusRow(this.last);
26985         }
26986     },
26987
26988     /**
26989      * Selects the row that precedes the last selected row.
26990      * @param {Boolean} keepExisting (optional) True to keep existing selections
26991      */
26992     selectPrevious : function(keepExisting){
26993         if(this.last){
26994             this.selectRow(this.last-1, keepExisting);
26995             this.grid.getView().focusRow(this.last);
26996         }
26997     },
26998
26999     /**
27000      * Returns the selected records
27001      * @return {Array} Array of selected records
27002      */
27003     getSelections : function(){
27004         return [].concat(this.selections.items);
27005     },
27006
27007     /**
27008      * Returns the first selected record.
27009      * @return {Record}
27010      */
27011     getSelected : function(){
27012         return this.selections.itemAt(0);
27013     },
27014
27015
27016     /**
27017      * Clears all selections.
27018      */
27019     clearSelections : function(fast)
27020     {
27021         if(this.locked) {
27022             return;
27023         }
27024         if(fast !== true){
27025                 var ds = this.grid.store;
27026             var s = this.selections;
27027             s.each(function(r){
27028                 this.deselectRow(ds.indexOfId(r.id));
27029             }, this);
27030             s.clear();
27031         }else{
27032             this.selections.clear();
27033         }
27034         this.last = false;
27035     },
27036
27037
27038     /**
27039      * Selects all rows.
27040      */
27041     selectAll : function(){
27042         if(this.locked) {
27043             return;
27044         }
27045         this.selections.clear();
27046         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27047             this.selectRow(i, true);
27048         }
27049     },
27050
27051     /**
27052      * Returns True if there is a selection.
27053      * @return {Boolean}
27054      */
27055     hasSelection : function(){
27056         return this.selections.length > 0;
27057     },
27058
27059     /**
27060      * Returns True if the specified row is selected.
27061      * @param {Number/Record} record The record or index of the record to check
27062      * @return {Boolean}
27063      */
27064     isSelected : function(index){
27065             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27066         return (r && this.selections.key(r.id) ? true : false);
27067     },
27068
27069     /**
27070      * Returns True if the specified record id is selected.
27071      * @param {String} id The id of record to check
27072      * @return {Boolean}
27073      */
27074     isIdSelected : function(id){
27075         return (this.selections.key(id) ? true : false);
27076     },
27077
27078
27079     // private
27080     handleMouseDBClick : function(e, t){
27081         
27082     },
27083     // private
27084     handleMouseDown : function(e, t)
27085     {
27086             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27087         if(this.isLocked() || rowIndex < 0 ){
27088             return;
27089         };
27090         if(e.shiftKey && this.last !== false){
27091             var last = this.last;
27092             this.selectRange(last, rowIndex, e.ctrlKey);
27093             this.last = last; // reset the last
27094             t.focus();
27095     
27096         }else{
27097             var isSelected = this.isSelected(rowIndex);
27098             //Roo.log("select row:" + rowIndex);
27099             if(isSelected){
27100                 this.deselectRow(rowIndex);
27101             } else {
27102                         this.selectRow(rowIndex, true);
27103             }
27104     
27105             /*
27106                 if(e.button !== 0 && isSelected){
27107                 alert('rowIndex 2: ' + rowIndex);
27108                     view.focusRow(rowIndex);
27109                 }else if(e.ctrlKey && isSelected){
27110                     this.deselectRow(rowIndex);
27111                 }else if(!isSelected){
27112                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27113                     view.focusRow(rowIndex);
27114                 }
27115             */
27116         }
27117         this.fireEvent("afterselectionchange", this);
27118     },
27119     // private
27120     handleDragableRowClick :  function(grid, rowIndex, e) 
27121     {
27122         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27123             this.selectRow(rowIndex, false);
27124             grid.view.focusRow(rowIndex);
27125              this.fireEvent("afterselectionchange", this);
27126         }
27127     },
27128     
27129     /**
27130      * Selects multiple rows.
27131      * @param {Array} rows Array of the indexes of the row to select
27132      * @param {Boolean} keepExisting (optional) True to keep existing selections
27133      */
27134     selectRows : function(rows, keepExisting){
27135         if(!keepExisting){
27136             this.clearSelections();
27137         }
27138         for(var i = 0, len = rows.length; i < len; i++){
27139             this.selectRow(rows[i], true);
27140         }
27141     },
27142
27143     /**
27144      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27145      * @param {Number} startRow The index of the first row in the range
27146      * @param {Number} endRow The index of the last row in the range
27147      * @param {Boolean} keepExisting (optional) True to retain existing selections
27148      */
27149     selectRange : function(startRow, endRow, keepExisting){
27150         if(this.locked) {
27151             return;
27152         }
27153         if(!keepExisting){
27154             this.clearSelections();
27155         }
27156         if(startRow <= endRow){
27157             for(var i = startRow; i <= endRow; i++){
27158                 this.selectRow(i, true);
27159             }
27160         }else{
27161             for(var i = startRow; i >= endRow; i--){
27162                 this.selectRow(i, true);
27163             }
27164         }
27165     },
27166
27167     /**
27168      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27169      * @param {Number} startRow The index of the first row in the range
27170      * @param {Number} endRow The index of the last row in the range
27171      */
27172     deselectRange : function(startRow, endRow, preventViewNotify){
27173         if(this.locked) {
27174             return;
27175         }
27176         for(var i = startRow; i <= endRow; i++){
27177             this.deselectRow(i, preventViewNotify);
27178         }
27179     },
27180
27181     /**
27182      * Selects a row.
27183      * @param {Number} row The index of the row to select
27184      * @param {Boolean} keepExisting (optional) True to keep existing selections
27185      */
27186     selectRow : function(index, keepExisting, preventViewNotify)
27187     {
27188             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27189             return;
27190         }
27191         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27192             if(!keepExisting || this.singleSelect){
27193                 this.clearSelections();
27194             }
27195             
27196             var r = this.grid.store.getAt(index);
27197             //console.log('selectRow - record id :' + r.id);
27198             
27199             this.selections.add(r);
27200             this.last = this.lastActive = index;
27201             if(!preventViewNotify){
27202                 var proxy = new Roo.Element(
27203                                 this.grid.getRowDom(index)
27204                 );
27205                 proxy.addClass('bg-info info');
27206             }
27207             this.fireEvent("rowselect", this, index, r);
27208             this.fireEvent("selectionchange", this);
27209         }
27210     },
27211
27212     /**
27213      * Deselects a row.
27214      * @param {Number} row The index of the row to deselect
27215      */
27216     deselectRow : function(index, preventViewNotify)
27217     {
27218         if(this.locked) {
27219             return;
27220         }
27221         if(this.last == index){
27222             this.last = false;
27223         }
27224         if(this.lastActive == index){
27225             this.lastActive = false;
27226         }
27227         
27228         var r = this.grid.store.getAt(index);
27229         if (!r) {
27230             return;
27231         }
27232         
27233         this.selections.remove(r);
27234         //.console.log('deselectRow - record id :' + r.id);
27235         if(!preventViewNotify){
27236         
27237             var proxy = new Roo.Element(
27238                 this.grid.getRowDom(index)
27239             );
27240             proxy.removeClass('bg-info info');
27241         }
27242         this.fireEvent("rowdeselect", this, index);
27243         this.fireEvent("selectionchange", this);
27244     },
27245
27246     // private
27247     restoreLast : function(){
27248         if(this._last){
27249             this.last = this._last;
27250         }
27251     },
27252
27253     // private
27254     acceptsNav : function(row, col, cm){
27255         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27256     },
27257
27258     // private
27259     onEditorKey : function(field, e){
27260         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27261         if(k == e.TAB){
27262             e.stopEvent();
27263             ed.completeEdit();
27264             if(e.shiftKey){
27265                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27266             }else{
27267                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27268             }
27269         }else if(k == e.ENTER && !e.ctrlKey){
27270             e.stopEvent();
27271             ed.completeEdit();
27272             if(e.shiftKey){
27273                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27274             }else{
27275                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27276             }
27277         }else if(k == e.ESC){
27278             ed.cancelEdit();
27279         }
27280         if(newCell){
27281             g.startEditing(newCell[0], newCell[1]);
27282         }
27283     }
27284 });
27285 /*
27286  * Based on:
27287  * Ext JS Library 1.1.1
27288  * Copyright(c) 2006-2007, Ext JS, LLC.
27289  *
27290  * Originally Released Under LGPL - original licence link has changed is not relivant.
27291  *
27292  * Fork - LGPL
27293  * <script type="text/javascript">
27294  */
27295  
27296 /**
27297  * @class Roo.bootstrap.PagingToolbar
27298  * @extends Roo.bootstrap.NavSimplebar
27299  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27300  * @constructor
27301  * Create a new PagingToolbar
27302  * @param {Object} config The config object
27303  * @param {Roo.data.Store} store
27304  */
27305 Roo.bootstrap.PagingToolbar = function(config)
27306 {
27307     // old args format still supported... - xtype is prefered..
27308         // created from xtype...
27309     
27310     this.ds = config.dataSource;
27311     
27312     if (config.store && !this.ds) {
27313         this.store= Roo.factory(config.store, Roo.data);
27314         this.ds = this.store;
27315         this.ds.xmodule = this.xmodule || false;
27316     }
27317     
27318     this.toolbarItems = [];
27319     if (config.items) {
27320         this.toolbarItems = config.items;
27321     }
27322     
27323     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27324     
27325     this.cursor = 0;
27326     
27327     if (this.ds) { 
27328         this.bind(this.ds);
27329     }
27330     
27331     if (Roo.bootstrap.version == 4) {
27332         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27333     } else {
27334         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27335     }
27336     
27337 };
27338
27339 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27340     /**
27341      * @cfg {Roo.data.Store} dataSource
27342      * The underlying data store providing the paged data
27343      */
27344     /**
27345      * @cfg {String/HTMLElement/Element} container
27346      * container The id or element that will contain the toolbar
27347      */
27348     /**
27349      * @cfg {Boolean} displayInfo
27350      * True to display the displayMsg (defaults to false)
27351      */
27352     /**
27353      * @cfg {Number} pageSize
27354      * The number of records to display per page (defaults to 20)
27355      */
27356     pageSize: 20,
27357     /**
27358      * @cfg {String} displayMsg
27359      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27360      */
27361     displayMsg : 'Displaying {0} - {1} of {2}',
27362     /**
27363      * @cfg {String} emptyMsg
27364      * The message to display when no records are found (defaults to "No data to display")
27365      */
27366     emptyMsg : 'No data to display',
27367     /**
27368      * Customizable piece of the default paging text (defaults to "Page")
27369      * @type String
27370      */
27371     beforePageText : "Page",
27372     /**
27373      * Customizable piece of the default paging text (defaults to "of %0")
27374      * @type String
27375      */
27376     afterPageText : "of {0}",
27377     /**
27378      * Customizable piece of the default paging text (defaults to "First Page")
27379      * @type String
27380      */
27381     firstText : "First Page",
27382     /**
27383      * Customizable piece of the default paging text (defaults to "Previous Page")
27384      * @type String
27385      */
27386     prevText : "Previous Page",
27387     /**
27388      * Customizable piece of the default paging text (defaults to "Next Page")
27389      * @type String
27390      */
27391     nextText : "Next Page",
27392     /**
27393      * Customizable piece of the default paging text (defaults to "Last Page")
27394      * @type String
27395      */
27396     lastText : "Last Page",
27397     /**
27398      * Customizable piece of the default paging text (defaults to "Refresh")
27399      * @type String
27400      */
27401     refreshText : "Refresh",
27402
27403     buttons : false,
27404     // private
27405     onRender : function(ct, position) 
27406     {
27407         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27408         this.navgroup.parentId = this.id;
27409         this.navgroup.onRender(this.el, null);
27410         // add the buttons to the navgroup
27411         
27412         if(this.displayInfo){
27413             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27414             this.displayEl = this.el.select('.x-paging-info', true).first();
27415 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27416 //            this.displayEl = navel.el.select('span',true).first();
27417         }
27418         
27419         var _this = this;
27420         
27421         if(this.buttons){
27422             Roo.each(_this.buttons, function(e){ // this might need to use render????
27423                Roo.factory(e).render(_this.el);
27424             });
27425         }
27426             
27427         Roo.each(_this.toolbarItems, function(e) {
27428             _this.navgroup.addItem(e);
27429         });
27430         
27431         
27432         this.first = this.navgroup.addItem({
27433             tooltip: this.firstText,
27434             cls: "prev btn-outline-secondary",
27435             html : ' <i class="fa fa-step-backward"></i>',
27436             disabled: true,
27437             preventDefault: true,
27438             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27439         });
27440         
27441         this.prev =  this.navgroup.addItem({
27442             tooltip: this.prevText,
27443             cls: "prev btn-outline-secondary",
27444             html : ' <i class="fa fa-backward"></i>',
27445             disabled: true,
27446             preventDefault: true,
27447             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27448         });
27449     //this.addSeparator();
27450         
27451         
27452         var field = this.navgroup.addItem( {
27453             tagtype : 'span',
27454             cls : 'x-paging-position  btn-outline-secondary',
27455              disabled: true,
27456             html : this.beforePageText  +
27457                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27458                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27459          } ); //?? escaped?
27460         
27461         this.field = field.el.select('input', true).first();
27462         this.field.on("keydown", this.onPagingKeydown, this);
27463         this.field.on("focus", function(){this.dom.select();});
27464     
27465     
27466         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27467         //this.field.setHeight(18);
27468         //this.addSeparator();
27469         this.next = this.navgroup.addItem({
27470             tooltip: this.nextText,
27471             cls: "next btn-outline-secondary",
27472             html : ' <i class="fa fa-forward"></i>',
27473             disabled: true,
27474             preventDefault: true,
27475             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27476         });
27477         this.last = this.navgroup.addItem({
27478             tooltip: this.lastText,
27479             html : ' <i class="fa fa-step-forward"></i>',
27480             cls: "next btn-outline-secondary",
27481             disabled: true,
27482             preventDefault: true,
27483             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27484         });
27485     //this.addSeparator();
27486         this.loading = this.navgroup.addItem({
27487             tooltip: this.refreshText,
27488             cls: "btn-outline-secondary",
27489             html : ' <i class="fa fa-refresh"></i>',
27490             preventDefault: true,
27491             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27492         });
27493         
27494     },
27495
27496     // private
27497     updateInfo : function(){
27498         if(this.displayEl){
27499             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27500             var msg = count == 0 ?
27501                 this.emptyMsg :
27502                 String.format(
27503                     this.displayMsg,
27504                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27505                 );
27506             this.displayEl.update(msg);
27507         }
27508     },
27509
27510     // private
27511     onLoad : function(ds, r, o)
27512     {
27513         this.cursor = o.params && o.params.start ? o.params.start : 0;
27514         
27515         var d = this.getPageData(),
27516             ap = d.activePage,
27517             ps = d.pages;
27518         
27519         
27520         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27521         this.field.dom.value = ap;
27522         this.first.setDisabled(ap == 1);
27523         this.prev.setDisabled(ap == 1);
27524         this.next.setDisabled(ap == ps);
27525         this.last.setDisabled(ap == ps);
27526         this.loading.enable();
27527         this.updateInfo();
27528     },
27529
27530     // private
27531     getPageData : function(){
27532         var total = this.ds.getTotalCount();
27533         return {
27534             total : total,
27535             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27536             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27537         };
27538     },
27539
27540     // private
27541     onLoadError : function(){
27542         this.loading.enable();
27543     },
27544
27545     // private
27546     onPagingKeydown : function(e){
27547         var k = e.getKey();
27548         var d = this.getPageData();
27549         if(k == e.RETURN){
27550             var v = this.field.dom.value, pageNum;
27551             if(!v || isNaN(pageNum = parseInt(v, 10))){
27552                 this.field.dom.value = d.activePage;
27553                 return;
27554             }
27555             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27556             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27557             e.stopEvent();
27558         }
27559         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))
27560         {
27561           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27562           this.field.dom.value = pageNum;
27563           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27564           e.stopEvent();
27565         }
27566         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27567         {
27568           var v = this.field.dom.value, pageNum; 
27569           var increment = (e.shiftKey) ? 10 : 1;
27570           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27571                 increment *= -1;
27572           }
27573           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27574             this.field.dom.value = d.activePage;
27575             return;
27576           }
27577           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27578           {
27579             this.field.dom.value = parseInt(v, 10) + increment;
27580             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27581             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27582           }
27583           e.stopEvent();
27584         }
27585     },
27586
27587     // private
27588     beforeLoad : function(){
27589         if(this.loading){
27590             this.loading.disable();
27591         }
27592     },
27593
27594     // private
27595     onClick : function(which){
27596         
27597         var ds = this.ds;
27598         if (!ds) {
27599             return;
27600         }
27601         
27602         switch(which){
27603             case "first":
27604                 ds.load({params:{start: 0, limit: this.pageSize}});
27605             break;
27606             case "prev":
27607                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27608             break;
27609             case "next":
27610                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27611             break;
27612             case "last":
27613                 var total = ds.getTotalCount();
27614                 var extra = total % this.pageSize;
27615                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27616                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27617             break;
27618             case "refresh":
27619                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27620             break;
27621         }
27622     },
27623
27624     /**
27625      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27626      * @param {Roo.data.Store} store The data store to unbind
27627      */
27628     unbind : function(ds){
27629         ds.un("beforeload", this.beforeLoad, this);
27630         ds.un("load", this.onLoad, this);
27631         ds.un("loadexception", this.onLoadError, this);
27632         ds.un("remove", this.updateInfo, this);
27633         ds.un("add", this.updateInfo, this);
27634         this.ds = undefined;
27635     },
27636
27637     /**
27638      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27639      * @param {Roo.data.Store} store The data store to bind
27640      */
27641     bind : function(ds){
27642         ds.on("beforeload", this.beforeLoad, this);
27643         ds.on("load", this.onLoad, this);
27644         ds.on("loadexception", this.onLoadError, this);
27645         ds.on("remove", this.updateInfo, this);
27646         ds.on("add", this.updateInfo, this);
27647         this.ds = ds;
27648     }
27649 });/*
27650  * - LGPL
27651  *
27652  * element
27653  * 
27654  */
27655
27656 /**
27657  * @class Roo.bootstrap.MessageBar
27658  * @extends Roo.bootstrap.Component
27659  * Bootstrap MessageBar class
27660  * @cfg {String} html contents of the MessageBar
27661  * @cfg {String} weight (info | success | warning | danger) default info
27662  * @cfg {String} beforeClass insert the bar before the given class
27663  * @cfg {Boolean} closable (true | false) default false
27664  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27665  * 
27666  * @constructor
27667  * Create a new Element
27668  * @param {Object} config The config object
27669  */
27670
27671 Roo.bootstrap.MessageBar = function(config){
27672     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27673 };
27674
27675 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27676     
27677     html: '',
27678     weight: 'info',
27679     closable: false,
27680     fixed: false,
27681     beforeClass: 'bootstrap-sticky-wrap',
27682     
27683     getAutoCreate : function(){
27684         
27685         var cfg = {
27686             tag: 'div',
27687             cls: 'alert alert-dismissable alert-' + this.weight,
27688             cn: [
27689                 {
27690                     tag: 'span',
27691                     cls: 'message',
27692                     html: this.html || ''
27693                 }
27694             ]
27695         };
27696         
27697         if(this.fixed){
27698             cfg.cls += ' alert-messages-fixed';
27699         }
27700         
27701         if(this.closable){
27702             cfg.cn.push({
27703                 tag: 'button',
27704                 cls: 'close',
27705                 html: 'x'
27706             });
27707         }
27708         
27709         return cfg;
27710     },
27711     
27712     onRender : function(ct, position)
27713     {
27714         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27715         
27716         if(!this.el){
27717             var cfg = Roo.apply({},  this.getAutoCreate());
27718             cfg.id = Roo.id();
27719             
27720             if (this.cls) {
27721                 cfg.cls += ' ' + this.cls;
27722             }
27723             if (this.style) {
27724                 cfg.style = this.style;
27725             }
27726             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27727             
27728             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27729         }
27730         
27731         this.el.select('>button.close').on('click', this.hide, this);
27732         
27733     },
27734     
27735     show : function()
27736     {
27737         if (!this.rendered) {
27738             this.render();
27739         }
27740         
27741         this.el.show();
27742         
27743         this.fireEvent('show', this);
27744         
27745     },
27746     
27747     hide : function()
27748     {
27749         if (!this.rendered) {
27750             this.render();
27751         }
27752         
27753         this.el.hide();
27754         
27755         this.fireEvent('hide', this);
27756     },
27757     
27758     update : function()
27759     {
27760 //        var e = this.el.dom.firstChild;
27761 //        
27762 //        if(this.closable){
27763 //            e = e.nextSibling;
27764 //        }
27765 //        
27766 //        e.data = this.html || '';
27767
27768         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27769     }
27770    
27771 });
27772
27773  
27774
27775      /*
27776  * - LGPL
27777  *
27778  * Graph
27779  * 
27780  */
27781
27782
27783 /**
27784  * @class Roo.bootstrap.Graph
27785  * @extends Roo.bootstrap.Component
27786  * Bootstrap Graph class
27787 > Prameters
27788  -sm {number} sm 4
27789  -md {number} md 5
27790  @cfg {String} graphtype  bar | vbar | pie
27791  @cfg {number} g_x coodinator | centre x (pie)
27792  @cfg {number} g_y coodinator | centre y (pie)
27793  @cfg {number} g_r radius (pie)
27794  @cfg {number} g_height height of the chart (respected by all elements in the set)
27795  @cfg {number} g_width width of the chart (respected by all elements in the set)
27796  @cfg {Object} title The title of the chart
27797     
27798  -{Array}  values
27799  -opts (object) options for the chart 
27800      o {
27801      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27802      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27803      o vgutter (number)
27804      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.
27805      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27806      o to
27807      o stretch (boolean)
27808      o }
27809  -opts (object) options for the pie
27810      o{
27811      o cut
27812      o startAngle (number)
27813      o endAngle (number)
27814      } 
27815  *
27816  * @constructor
27817  * Create a new Input
27818  * @param {Object} config The config object
27819  */
27820
27821 Roo.bootstrap.Graph = function(config){
27822     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27823     
27824     this.addEvents({
27825         // img events
27826         /**
27827          * @event click
27828          * The img click event for the img.
27829          * @param {Roo.EventObject} e
27830          */
27831         "click" : true
27832     });
27833 };
27834
27835 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27836     
27837     sm: 4,
27838     md: 5,
27839     graphtype: 'bar',
27840     g_height: 250,
27841     g_width: 400,
27842     g_x: 50,
27843     g_y: 50,
27844     g_r: 30,
27845     opts:{
27846         //g_colors: this.colors,
27847         g_type: 'soft',
27848         g_gutter: '20%'
27849
27850     },
27851     title : false,
27852
27853     getAutoCreate : function(){
27854         
27855         var cfg = {
27856             tag: 'div',
27857             html : null
27858         };
27859         
27860         
27861         return  cfg;
27862     },
27863
27864     onRender : function(ct,position){
27865         
27866         
27867         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27868         
27869         if (typeof(Raphael) == 'undefined') {
27870             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27871             return;
27872         }
27873         
27874         this.raphael = Raphael(this.el.dom);
27875         
27876                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27877                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27878                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27879                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27880                 /*
27881                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27882                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27883                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27884                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27885                 
27886                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27887                 r.barchart(330, 10, 300, 220, data1);
27888                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27889                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27890                 */
27891                 
27892                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27893                 // r.barchart(30, 30, 560, 250,  xdata, {
27894                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27895                 //     axis : "0 0 1 1",
27896                 //     axisxlabels :  xdata
27897                 //     //yvalues : cols,
27898                    
27899                 // });
27900 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27901 //        
27902 //        this.load(null,xdata,{
27903 //                axis : "0 0 1 1",
27904 //                axisxlabels :  xdata
27905 //                });
27906
27907     },
27908
27909     load : function(graphtype,xdata,opts)
27910     {
27911         this.raphael.clear();
27912         if(!graphtype) {
27913             graphtype = this.graphtype;
27914         }
27915         if(!opts){
27916             opts = this.opts;
27917         }
27918         var r = this.raphael,
27919             fin = function () {
27920                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27921             },
27922             fout = function () {
27923                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27924             },
27925             pfin = function() {
27926                 this.sector.stop();
27927                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27928
27929                 if (this.label) {
27930                     this.label[0].stop();
27931                     this.label[0].attr({ r: 7.5 });
27932                     this.label[1].attr({ "font-weight": 800 });
27933                 }
27934             },
27935             pfout = function() {
27936                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27937
27938                 if (this.label) {
27939                     this.label[0].animate({ r: 5 }, 500, "bounce");
27940                     this.label[1].attr({ "font-weight": 400 });
27941                 }
27942             };
27943
27944         switch(graphtype){
27945             case 'bar':
27946                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27947                 break;
27948             case 'hbar':
27949                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27950                 break;
27951             case 'pie':
27952 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27953 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27954 //            
27955                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27956                 
27957                 break;
27958
27959         }
27960         
27961         if(this.title){
27962             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27963         }
27964         
27965     },
27966     
27967     setTitle: function(o)
27968     {
27969         this.title = o;
27970     },
27971     
27972     initEvents: function() {
27973         
27974         if(!this.href){
27975             this.el.on('click', this.onClick, this);
27976         }
27977     },
27978     
27979     onClick : function(e)
27980     {
27981         Roo.log('img onclick');
27982         this.fireEvent('click', this, e);
27983     }
27984    
27985 });
27986
27987  
27988 /*
27989  * - LGPL
27990  *
27991  * numberBox
27992  * 
27993  */
27994 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27995
27996 /**
27997  * @class Roo.bootstrap.dash.NumberBox
27998  * @extends Roo.bootstrap.Component
27999  * Bootstrap NumberBox class
28000  * @cfg {String} headline Box headline
28001  * @cfg {String} content Box content
28002  * @cfg {String} icon Box icon
28003  * @cfg {String} footer Footer text
28004  * @cfg {String} fhref Footer href
28005  * 
28006  * @constructor
28007  * Create a new NumberBox
28008  * @param {Object} config The config object
28009  */
28010
28011
28012 Roo.bootstrap.dash.NumberBox = function(config){
28013     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28014     
28015 };
28016
28017 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28018     
28019     headline : '',
28020     content : '',
28021     icon : '',
28022     footer : '',
28023     fhref : '',
28024     ficon : '',
28025     
28026     getAutoCreate : function(){
28027         
28028         var cfg = {
28029             tag : 'div',
28030             cls : 'small-box ',
28031             cn : [
28032                 {
28033                     tag : 'div',
28034                     cls : 'inner',
28035                     cn :[
28036                         {
28037                             tag : 'h3',
28038                             cls : 'roo-headline',
28039                             html : this.headline
28040                         },
28041                         {
28042                             tag : 'p',
28043                             cls : 'roo-content',
28044                             html : this.content
28045                         }
28046                     ]
28047                 }
28048             ]
28049         };
28050         
28051         if(this.icon){
28052             cfg.cn.push({
28053                 tag : 'div',
28054                 cls : 'icon',
28055                 cn :[
28056                     {
28057                         tag : 'i',
28058                         cls : 'ion ' + this.icon
28059                     }
28060                 ]
28061             });
28062         }
28063         
28064         if(this.footer){
28065             var footer = {
28066                 tag : 'a',
28067                 cls : 'small-box-footer',
28068                 href : this.fhref || '#',
28069                 html : this.footer
28070             };
28071             
28072             cfg.cn.push(footer);
28073             
28074         }
28075         
28076         return  cfg;
28077     },
28078
28079     onRender : function(ct,position){
28080         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28081
28082
28083        
28084                 
28085     },
28086
28087     setHeadline: function (value)
28088     {
28089         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28090     },
28091     
28092     setFooter: function (value, href)
28093     {
28094         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28095         
28096         if(href){
28097             this.el.select('a.small-box-footer',true).first().attr('href', href);
28098         }
28099         
28100     },
28101
28102     setContent: function (value)
28103     {
28104         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28105     },
28106
28107     initEvents: function() 
28108     {   
28109         
28110     }
28111     
28112 });
28113
28114  
28115 /*
28116  * - LGPL
28117  *
28118  * TabBox
28119  * 
28120  */
28121 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28122
28123 /**
28124  * @class Roo.bootstrap.dash.TabBox
28125  * @extends Roo.bootstrap.Component
28126  * Bootstrap TabBox class
28127  * @cfg {String} title Title of the TabBox
28128  * @cfg {String} icon Icon of the TabBox
28129  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28130  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28131  * 
28132  * @constructor
28133  * Create a new TabBox
28134  * @param {Object} config The config object
28135  */
28136
28137
28138 Roo.bootstrap.dash.TabBox = function(config){
28139     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28140     this.addEvents({
28141         // raw events
28142         /**
28143          * @event addpane
28144          * When a pane is added
28145          * @param {Roo.bootstrap.dash.TabPane} pane
28146          */
28147         "addpane" : true,
28148         /**
28149          * @event activatepane
28150          * When a pane is activated
28151          * @param {Roo.bootstrap.dash.TabPane} pane
28152          */
28153         "activatepane" : true
28154         
28155          
28156     });
28157     
28158     this.panes = [];
28159 };
28160
28161 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28162
28163     title : '',
28164     icon : false,
28165     showtabs : true,
28166     tabScrollable : false,
28167     
28168     getChildContainer : function()
28169     {
28170         return this.el.select('.tab-content', true).first();
28171     },
28172     
28173     getAutoCreate : function(){
28174         
28175         var header = {
28176             tag: 'li',
28177             cls: 'pull-left header',
28178             html: this.title,
28179             cn : []
28180         };
28181         
28182         if(this.icon){
28183             header.cn.push({
28184                 tag: 'i',
28185                 cls: 'fa ' + this.icon
28186             });
28187         }
28188         
28189         var h = {
28190             tag: 'ul',
28191             cls: 'nav nav-tabs pull-right',
28192             cn: [
28193                 header
28194             ]
28195         };
28196         
28197         if(this.tabScrollable){
28198             h = {
28199                 tag: 'div',
28200                 cls: 'tab-header',
28201                 cn: [
28202                     {
28203                         tag: 'ul',
28204                         cls: 'nav nav-tabs pull-right',
28205                         cn: [
28206                             header
28207                         ]
28208                     }
28209                 ]
28210             };
28211         }
28212         
28213         var cfg = {
28214             tag: 'div',
28215             cls: 'nav-tabs-custom',
28216             cn: [
28217                 h,
28218                 {
28219                     tag: 'div',
28220                     cls: 'tab-content no-padding',
28221                     cn: []
28222                 }
28223             ]
28224         };
28225
28226         return  cfg;
28227     },
28228     initEvents : function()
28229     {
28230         //Roo.log('add add pane handler');
28231         this.on('addpane', this.onAddPane, this);
28232     },
28233      /**
28234      * Updates the box title
28235      * @param {String} html to set the title to.
28236      */
28237     setTitle : function(value)
28238     {
28239         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28240     },
28241     onAddPane : function(pane)
28242     {
28243         this.panes.push(pane);
28244         //Roo.log('addpane');
28245         //Roo.log(pane);
28246         // tabs are rendere left to right..
28247         if(!this.showtabs){
28248             return;
28249         }
28250         
28251         var ctr = this.el.select('.nav-tabs', true).first();
28252          
28253          
28254         var existing = ctr.select('.nav-tab',true);
28255         var qty = existing.getCount();;
28256         
28257         
28258         var tab = ctr.createChild({
28259             tag : 'li',
28260             cls : 'nav-tab' + (qty ? '' : ' active'),
28261             cn : [
28262                 {
28263                     tag : 'a',
28264                     href:'#',
28265                     html : pane.title
28266                 }
28267             ]
28268         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28269         pane.tab = tab;
28270         
28271         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28272         if (!qty) {
28273             pane.el.addClass('active');
28274         }
28275         
28276                 
28277     },
28278     onTabClick : function(ev,un,ob,pane)
28279     {
28280         //Roo.log('tab - prev default');
28281         ev.preventDefault();
28282         
28283         
28284         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28285         pane.tab.addClass('active');
28286         //Roo.log(pane.title);
28287         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28288         // technically we should have a deactivate event.. but maybe add later.
28289         // and it should not de-activate the selected tab...
28290         this.fireEvent('activatepane', pane);
28291         pane.el.addClass('active');
28292         pane.fireEvent('activate');
28293         
28294         
28295     },
28296     
28297     getActivePane : function()
28298     {
28299         var r = false;
28300         Roo.each(this.panes, function(p) {
28301             if(p.el.hasClass('active')){
28302                 r = p;
28303                 return false;
28304             }
28305             
28306             return;
28307         });
28308         
28309         return r;
28310     }
28311     
28312     
28313 });
28314
28315  
28316 /*
28317  * - LGPL
28318  *
28319  * Tab pane
28320  * 
28321  */
28322 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28323 /**
28324  * @class Roo.bootstrap.TabPane
28325  * @extends Roo.bootstrap.Component
28326  * Bootstrap TabPane class
28327  * @cfg {Boolean} active (false | true) Default false
28328  * @cfg {String} title title of panel
28329
28330  * 
28331  * @constructor
28332  * Create a new TabPane
28333  * @param {Object} config The config object
28334  */
28335
28336 Roo.bootstrap.dash.TabPane = function(config){
28337     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28338     
28339     this.addEvents({
28340         // raw events
28341         /**
28342          * @event activate
28343          * When a pane is activated
28344          * @param {Roo.bootstrap.dash.TabPane} pane
28345          */
28346         "activate" : true
28347          
28348     });
28349 };
28350
28351 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28352     
28353     active : false,
28354     title : '',
28355     
28356     // the tabBox that this is attached to.
28357     tab : false,
28358      
28359     getAutoCreate : function() 
28360     {
28361         var cfg = {
28362             tag: 'div',
28363             cls: 'tab-pane'
28364         };
28365         
28366         if(this.active){
28367             cfg.cls += ' active';
28368         }
28369         
28370         return cfg;
28371     },
28372     initEvents  : function()
28373     {
28374         //Roo.log('trigger add pane handler');
28375         this.parent().fireEvent('addpane', this)
28376     },
28377     
28378      /**
28379      * Updates the tab title 
28380      * @param {String} html to set the title to.
28381      */
28382     setTitle: function(str)
28383     {
28384         if (!this.tab) {
28385             return;
28386         }
28387         this.title = str;
28388         this.tab.select('a', true).first().dom.innerHTML = str;
28389         
28390     }
28391     
28392     
28393     
28394 });
28395
28396  
28397
28398
28399  /*
28400  * - LGPL
28401  *
28402  * menu
28403  * 
28404  */
28405 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28406
28407 /**
28408  * @class Roo.bootstrap.menu.Menu
28409  * @extends Roo.bootstrap.Component
28410  * Bootstrap Menu class - container for Menu
28411  * @cfg {String} html Text of the menu
28412  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28413  * @cfg {String} icon Font awesome icon
28414  * @cfg {String} pos Menu align to (top | bottom) default bottom
28415  * 
28416  * 
28417  * @constructor
28418  * Create a new Menu
28419  * @param {Object} config The config object
28420  */
28421
28422
28423 Roo.bootstrap.menu.Menu = function(config){
28424     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28425     
28426     this.addEvents({
28427         /**
28428          * @event beforeshow
28429          * Fires before this menu is displayed
28430          * @param {Roo.bootstrap.menu.Menu} this
28431          */
28432         beforeshow : true,
28433         /**
28434          * @event beforehide
28435          * Fires before this menu is hidden
28436          * @param {Roo.bootstrap.menu.Menu} this
28437          */
28438         beforehide : true,
28439         /**
28440          * @event show
28441          * Fires after this menu is displayed
28442          * @param {Roo.bootstrap.menu.Menu} this
28443          */
28444         show : true,
28445         /**
28446          * @event hide
28447          * Fires after this menu is hidden
28448          * @param {Roo.bootstrap.menu.Menu} this
28449          */
28450         hide : true,
28451         /**
28452          * @event click
28453          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28454          * @param {Roo.bootstrap.menu.Menu} this
28455          * @param {Roo.EventObject} e
28456          */
28457         click : true
28458     });
28459     
28460 };
28461
28462 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28463     
28464     submenu : false,
28465     html : '',
28466     weight : 'default',
28467     icon : false,
28468     pos : 'bottom',
28469     
28470     
28471     getChildContainer : function() {
28472         if(this.isSubMenu){
28473             return this.el;
28474         }
28475         
28476         return this.el.select('ul.dropdown-menu', true).first();  
28477     },
28478     
28479     getAutoCreate : function()
28480     {
28481         var text = [
28482             {
28483                 tag : 'span',
28484                 cls : 'roo-menu-text',
28485                 html : this.html
28486             }
28487         ];
28488         
28489         if(this.icon){
28490             text.unshift({
28491                 tag : 'i',
28492                 cls : 'fa ' + this.icon
28493             })
28494         }
28495         
28496         
28497         var cfg = {
28498             tag : 'div',
28499             cls : 'btn-group',
28500             cn : [
28501                 {
28502                     tag : 'button',
28503                     cls : 'dropdown-button btn btn-' + this.weight,
28504                     cn : text
28505                 },
28506                 {
28507                     tag : 'button',
28508                     cls : 'dropdown-toggle btn btn-' + this.weight,
28509                     cn : [
28510                         {
28511                             tag : 'span',
28512                             cls : 'caret'
28513                         }
28514                     ]
28515                 },
28516                 {
28517                     tag : 'ul',
28518                     cls : 'dropdown-menu'
28519                 }
28520             ]
28521             
28522         };
28523         
28524         if(this.pos == 'top'){
28525             cfg.cls += ' dropup';
28526         }
28527         
28528         if(this.isSubMenu){
28529             cfg = {
28530                 tag : 'ul',
28531                 cls : 'dropdown-menu'
28532             }
28533         }
28534         
28535         return cfg;
28536     },
28537     
28538     onRender : function(ct, position)
28539     {
28540         this.isSubMenu = ct.hasClass('dropdown-submenu');
28541         
28542         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28543     },
28544     
28545     initEvents : function() 
28546     {
28547         if(this.isSubMenu){
28548             return;
28549         }
28550         
28551         this.hidden = true;
28552         
28553         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28554         this.triggerEl.on('click', this.onTriggerPress, this);
28555         
28556         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28557         this.buttonEl.on('click', this.onClick, this);
28558         
28559     },
28560     
28561     list : function()
28562     {
28563         if(this.isSubMenu){
28564             return this.el;
28565         }
28566         
28567         return this.el.select('ul.dropdown-menu', true).first();
28568     },
28569     
28570     onClick : function(e)
28571     {
28572         this.fireEvent("click", this, e);
28573     },
28574     
28575     onTriggerPress  : function(e)
28576     {   
28577         if (this.isVisible()) {
28578             this.hide();
28579         } else {
28580             this.show();
28581         }
28582     },
28583     
28584     isVisible : function(){
28585         return !this.hidden;
28586     },
28587     
28588     show : function()
28589     {
28590         this.fireEvent("beforeshow", this);
28591         
28592         this.hidden = false;
28593         this.el.addClass('open');
28594         
28595         Roo.get(document).on("mouseup", this.onMouseUp, this);
28596         
28597         this.fireEvent("show", this);
28598         
28599         
28600     },
28601     
28602     hide : function()
28603     {
28604         this.fireEvent("beforehide", this);
28605         
28606         this.hidden = true;
28607         this.el.removeClass('open');
28608         
28609         Roo.get(document).un("mouseup", this.onMouseUp);
28610         
28611         this.fireEvent("hide", this);
28612     },
28613     
28614     onMouseUp : function()
28615     {
28616         this.hide();
28617     }
28618     
28619 });
28620
28621  
28622  /*
28623  * - LGPL
28624  *
28625  * menu item
28626  * 
28627  */
28628 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28629
28630 /**
28631  * @class Roo.bootstrap.menu.Item
28632  * @extends Roo.bootstrap.Component
28633  * Bootstrap MenuItem class
28634  * @cfg {Boolean} submenu (true | false) default false
28635  * @cfg {String} html text of the item
28636  * @cfg {String} href the link
28637  * @cfg {Boolean} disable (true | false) default false
28638  * @cfg {Boolean} preventDefault (true | false) default true
28639  * @cfg {String} icon Font awesome icon
28640  * @cfg {String} pos Submenu align to (left | right) default right 
28641  * 
28642  * 
28643  * @constructor
28644  * Create a new Item
28645  * @param {Object} config The config object
28646  */
28647
28648
28649 Roo.bootstrap.menu.Item = function(config){
28650     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28651     this.addEvents({
28652         /**
28653          * @event mouseover
28654          * Fires when the mouse is hovering over this menu
28655          * @param {Roo.bootstrap.menu.Item} this
28656          * @param {Roo.EventObject} e
28657          */
28658         mouseover : true,
28659         /**
28660          * @event mouseout
28661          * Fires when the mouse exits this menu
28662          * @param {Roo.bootstrap.menu.Item} this
28663          * @param {Roo.EventObject} e
28664          */
28665         mouseout : true,
28666         // raw events
28667         /**
28668          * @event click
28669          * The raw click event for the entire grid.
28670          * @param {Roo.EventObject} e
28671          */
28672         click : true
28673     });
28674 };
28675
28676 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28677     
28678     submenu : false,
28679     href : '',
28680     html : '',
28681     preventDefault: true,
28682     disable : false,
28683     icon : false,
28684     pos : 'right',
28685     
28686     getAutoCreate : function()
28687     {
28688         var text = [
28689             {
28690                 tag : 'span',
28691                 cls : 'roo-menu-item-text',
28692                 html : this.html
28693             }
28694         ];
28695         
28696         if(this.icon){
28697             text.unshift({
28698                 tag : 'i',
28699                 cls : 'fa ' + this.icon
28700             })
28701         }
28702         
28703         var cfg = {
28704             tag : 'li',
28705             cn : [
28706                 {
28707                     tag : 'a',
28708                     href : this.href || '#',
28709                     cn : text
28710                 }
28711             ]
28712         };
28713         
28714         if(this.disable){
28715             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28716         }
28717         
28718         if(this.submenu){
28719             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28720             
28721             if(this.pos == 'left'){
28722                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28723             }
28724         }
28725         
28726         return cfg;
28727     },
28728     
28729     initEvents : function() 
28730     {
28731         this.el.on('mouseover', this.onMouseOver, this);
28732         this.el.on('mouseout', this.onMouseOut, this);
28733         
28734         this.el.select('a', true).first().on('click', this.onClick, this);
28735         
28736     },
28737     
28738     onClick : function(e)
28739     {
28740         if(this.preventDefault){
28741             e.preventDefault();
28742         }
28743         
28744         this.fireEvent("click", this, e);
28745     },
28746     
28747     onMouseOver : function(e)
28748     {
28749         if(this.submenu && this.pos == 'left'){
28750             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28751         }
28752         
28753         this.fireEvent("mouseover", this, e);
28754     },
28755     
28756     onMouseOut : function(e)
28757     {
28758         this.fireEvent("mouseout", this, e);
28759     }
28760 });
28761
28762  
28763
28764  /*
28765  * - LGPL
28766  *
28767  * menu separator
28768  * 
28769  */
28770 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28771
28772 /**
28773  * @class Roo.bootstrap.menu.Separator
28774  * @extends Roo.bootstrap.Component
28775  * Bootstrap Separator class
28776  * 
28777  * @constructor
28778  * Create a new Separator
28779  * @param {Object} config The config object
28780  */
28781
28782
28783 Roo.bootstrap.menu.Separator = function(config){
28784     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28785 };
28786
28787 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28788     
28789     getAutoCreate : function(){
28790         var cfg = {
28791             tag : 'li',
28792             cls: 'divider'
28793         };
28794         
28795         return cfg;
28796     }
28797    
28798 });
28799
28800  
28801
28802  /*
28803  * - LGPL
28804  *
28805  * Tooltip
28806  * 
28807  */
28808
28809 /**
28810  * @class Roo.bootstrap.Tooltip
28811  * Bootstrap Tooltip class
28812  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28813  * to determine which dom element triggers the tooltip.
28814  * 
28815  * It needs to add support for additional attributes like tooltip-position
28816  * 
28817  * @constructor
28818  * Create a new Toolti
28819  * @param {Object} config The config object
28820  */
28821
28822 Roo.bootstrap.Tooltip = function(config){
28823     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28824     
28825     this.alignment = Roo.bootstrap.Tooltip.alignment;
28826     
28827     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28828         this.alignment = config.alignment;
28829     }
28830     
28831 };
28832
28833 Roo.apply(Roo.bootstrap.Tooltip, {
28834     /**
28835      * @function init initialize tooltip monitoring.
28836      * @static
28837      */
28838     currentEl : false,
28839     currentTip : false,
28840     currentRegion : false,
28841     
28842     //  init : delay?
28843     
28844     init : function()
28845     {
28846         Roo.get(document).on('mouseover', this.enter ,this);
28847         Roo.get(document).on('mouseout', this.leave, this);
28848          
28849         
28850         this.currentTip = new Roo.bootstrap.Tooltip();
28851     },
28852     
28853     enter : function(ev)
28854     {
28855         var dom = ev.getTarget();
28856         
28857         //Roo.log(['enter',dom]);
28858         var el = Roo.fly(dom);
28859         if (this.currentEl) {
28860             //Roo.log(dom);
28861             //Roo.log(this.currentEl);
28862             //Roo.log(this.currentEl.contains(dom));
28863             if (this.currentEl == el) {
28864                 return;
28865             }
28866             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28867                 return;
28868             }
28869
28870         }
28871         
28872         if (this.currentTip.el) {
28873             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28874         }    
28875         //Roo.log(ev);
28876         
28877         if(!el || el.dom == document){
28878             return;
28879         }
28880         
28881         var bindEl = el;
28882         
28883         // you can not look for children, as if el is the body.. then everythign is the child..
28884         if (!el.attr('tooltip')) { //
28885             if (!el.select("[tooltip]").elements.length) {
28886                 return;
28887             }
28888             // is the mouse over this child...?
28889             bindEl = el.select("[tooltip]").first();
28890             var xy = ev.getXY();
28891             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28892                 //Roo.log("not in region.");
28893                 return;
28894             }
28895             //Roo.log("child element over..");
28896             
28897         }
28898         this.currentEl = bindEl;
28899         this.currentTip.bind(bindEl);
28900         this.currentRegion = Roo.lib.Region.getRegion(dom);
28901         this.currentTip.enter();
28902         
28903     },
28904     leave : function(ev)
28905     {
28906         var dom = ev.getTarget();
28907         //Roo.log(['leave',dom]);
28908         if (!this.currentEl) {
28909             return;
28910         }
28911         
28912         
28913         if (dom != this.currentEl.dom) {
28914             return;
28915         }
28916         var xy = ev.getXY();
28917         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28918             return;
28919         }
28920         // only activate leave if mouse cursor is outside... bounding box..
28921         
28922         
28923         
28924         
28925         if (this.currentTip) {
28926             this.currentTip.leave();
28927         }
28928         //Roo.log('clear currentEl');
28929         this.currentEl = false;
28930         
28931         
28932     },
28933     alignment : {
28934         'left' : ['r-l', [-2,0], 'right'],
28935         'right' : ['l-r', [2,0], 'left'],
28936         'bottom' : ['t-b', [0,2], 'top'],
28937         'top' : [ 'b-t', [0,-2], 'bottom']
28938     }
28939     
28940 });
28941
28942
28943 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28944     
28945     
28946     bindEl : false,
28947     
28948     delay : null, // can be { show : 300 , hide: 500}
28949     
28950     timeout : null,
28951     
28952     hoverState : null, //???
28953     
28954     placement : 'bottom', 
28955     
28956     alignment : false,
28957     
28958     getAutoCreate : function(){
28959     
28960         var cfg = {
28961            cls : 'tooltip',   
28962            role : 'tooltip',
28963            cn : [
28964                 {
28965                     cls : 'tooltip-arrow arrow'
28966                 },
28967                 {
28968                     cls : 'tooltip-inner'
28969                 }
28970            ]
28971         };
28972         
28973         return cfg;
28974     },
28975     bind : function(el)
28976     {
28977         this.bindEl = el;
28978     },
28979     
28980     initEvents : function()
28981     {
28982         this.arrowEl = this.el.select('.arrow', true).first();
28983         this.innerEl = this.el.select('.tooltip-inner', true).first();
28984     },
28985     
28986     enter : function () {
28987        
28988         if (this.timeout != null) {
28989             clearTimeout(this.timeout);
28990         }
28991         
28992         this.hoverState = 'in';
28993          //Roo.log("enter - show");
28994         if (!this.delay || !this.delay.show) {
28995             this.show();
28996             return;
28997         }
28998         var _t = this;
28999         this.timeout = setTimeout(function () {
29000             if (_t.hoverState == 'in') {
29001                 _t.show();
29002             }
29003         }, this.delay.show);
29004     },
29005     leave : function()
29006     {
29007         clearTimeout(this.timeout);
29008     
29009         this.hoverState = 'out';
29010          if (!this.delay || !this.delay.hide) {
29011             this.hide();
29012             return;
29013         }
29014        
29015         var _t = this;
29016         this.timeout = setTimeout(function () {
29017             //Roo.log("leave - timeout");
29018             
29019             if (_t.hoverState == 'out') {
29020                 _t.hide();
29021                 Roo.bootstrap.Tooltip.currentEl = false;
29022             }
29023         }, delay);
29024     },
29025     
29026     show : function (msg)
29027     {
29028         if (!this.el) {
29029             this.render(document.body);
29030         }
29031         // set content.
29032         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29033         
29034         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29035         
29036         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29037         
29038         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29039                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29040         
29041         var placement = typeof this.placement == 'function' ?
29042             this.placement.call(this, this.el, on_el) :
29043             this.placement;
29044             
29045         var autoToken = /\s?auto?\s?/i;
29046         var autoPlace = autoToken.test(placement);
29047         if (autoPlace) {
29048             placement = placement.replace(autoToken, '') || 'top';
29049         }
29050         
29051         //this.el.detach()
29052         //this.el.setXY([0,0]);
29053         this.el.show();
29054         //this.el.dom.style.display='block';
29055         
29056         //this.el.appendTo(on_el);
29057         
29058         var p = this.getPosition();
29059         var box = this.el.getBox();
29060         
29061         if (autoPlace) {
29062             // fixme..
29063         }
29064         
29065         var align = this.alignment[placement];
29066         
29067         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29068         
29069         if(placement == 'top' || placement == 'bottom'){
29070             if(xy[0] < 0){
29071                 placement = 'right';
29072             }
29073             
29074             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29075                 placement = 'left';
29076             }
29077             
29078             var scroll = Roo.select('body', true).first().getScroll();
29079             
29080             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29081                 placement = 'top';
29082             }
29083             
29084             align = this.alignment[placement];
29085             
29086             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29087             
29088         }
29089         
29090         this.el.alignTo(this.bindEl, align[0],align[1]);
29091         //var arrow = this.el.select('.arrow',true).first();
29092         //arrow.set(align[2], 
29093         
29094         this.el.addClass(placement);
29095         this.el.addClass("bs-tooltip-"+ placement);
29096         
29097         this.el.addClass('in fade show');
29098         
29099         this.hoverState = null;
29100         
29101         if (this.el.hasClass('fade')) {
29102             // fade it?
29103         }
29104         
29105         
29106         
29107         
29108         
29109     },
29110     hide : function()
29111     {
29112          
29113         if (!this.el) {
29114             return;
29115         }
29116         //this.el.setXY([0,0]);
29117         this.el.removeClass(['show', 'in']);
29118         //this.el.hide();
29119         
29120     }
29121     
29122 });
29123  
29124
29125  /*
29126  * - LGPL
29127  *
29128  * Location Picker
29129  * 
29130  */
29131
29132 /**
29133  * @class Roo.bootstrap.LocationPicker
29134  * @extends Roo.bootstrap.Component
29135  * Bootstrap LocationPicker class
29136  * @cfg {Number} latitude Position when init default 0
29137  * @cfg {Number} longitude Position when init default 0
29138  * @cfg {Number} zoom default 15
29139  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29140  * @cfg {Boolean} mapTypeControl default false
29141  * @cfg {Boolean} disableDoubleClickZoom default false
29142  * @cfg {Boolean} scrollwheel default true
29143  * @cfg {Boolean} streetViewControl default false
29144  * @cfg {Number} radius default 0
29145  * @cfg {String} locationName
29146  * @cfg {Boolean} draggable default true
29147  * @cfg {Boolean} enableAutocomplete default false
29148  * @cfg {Boolean} enableReverseGeocode default true
29149  * @cfg {String} markerTitle
29150  * 
29151  * @constructor
29152  * Create a new LocationPicker
29153  * @param {Object} config The config object
29154  */
29155
29156
29157 Roo.bootstrap.LocationPicker = function(config){
29158     
29159     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29160     
29161     this.addEvents({
29162         /**
29163          * @event initial
29164          * Fires when the picker initialized.
29165          * @param {Roo.bootstrap.LocationPicker} this
29166          * @param {Google Location} location
29167          */
29168         initial : true,
29169         /**
29170          * @event positionchanged
29171          * Fires when the picker position changed.
29172          * @param {Roo.bootstrap.LocationPicker} this
29173          * @param {Google Location} location
29174          */
29175         positionchanged : true,
29176         /**
29177          * @event resize
29178          * Fires when the map resize.
29179          * @param {Roo.bootstrap.LocationPicker} this
29180          */
29181         resize : true,
29182         /**
29183          * @event show
29184          * Fires when the map show.
29185          * @param {Roo.bootstrap.LocationPicker} this
29186          */
29187         show : true,
29188         /**
29189          * @event hide
29190          * Fires when the map hide.
29191          * @param {Roo.bootstrap.LocationPicker} this
29192          */
29193         hide : true,
29194         /**
29195          * @event mapClick
29196          * Fires when click the map.
29197          * @param {Roo.bootstrap.LocationPicker} this
29198          * @param {Map event} e
29199          */
29200         mapClick : true,
29201         /**
29202          * @event mapRightClick
29203          * Fires when right click the map.
29204          * @param {Roo.bootstrap.LocationPicker} this
29205          * @param {Map event} e
29206          */
29207         mapRightClick : true,
29208         /**
29209          * @event markerClick
29210          * Fires when click the marker.
29211          * @param {Roo.bootstrap.LocationPicker} this
29212          * @param {Map event} e
29213          */
29214         markerClick : true,
29215         /**
29216          * @event markerRightClick
29217          * Fires when right click the marker.
29218          * @param {Roo.bootstrap.LocationPicker} this
29219          * @param {Map event} e
29220          */
29221         markerRightClick : true,
29222         /**
29223          * @event OverlayViewDraw
29224          * Fires when OverlayView Draw
29225          * @param {Roo.bootstrap.LocationPicker} this
29226          */
29227         OverlayViewDraw : true,
29228         /**
29229          * @event OverlayViewOnAdd
29230          * Fires when OverlayView Draw
29231          * @param {Roo.bootstrap.LocationPicker} this
29232          */
29233         OverlayViewOnAdd : true,
29234         /**
29235          * @event OverlayViewOnRemove
29236          * Fires when OverlayView Draw
29237          * @param {Roo.bootstrap.LocationPicker} this
29238          */
29239         OverlayViewOnRemove : true,
29240         /**
29241          * @event OverlayViewShow
29242          * Fires when OverlayView Draw
29243          * @param {Roo.bootstrap.LocationPicker} this
29244          * @param {Pixel} cpx
29245          */
29246         OverlayViewShow : true,
29247         /**
29248          * @event OverlayViewHide
29249          * Fires when OverlayView Draw
29250          * @param {Roo.bootstrap.LocationPicker} this
29251          */
29252         OverlayViewHide : true,
29253         /**
29254          * @event loadexception
29255          * Fires when load google lib failed.
29256          * @param {Roo.bootstrap.LocationPicker} this
29257          */
29258         loadexception : true
29259     });
29260         
29261 };
29262
29263 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29264     
29265     gMapContext: false,
29266     
29267     latitude: 0,
29268     longitude: 0,
29269     zoom: 15,
29270     mapTypeId: false,
29271     mapTypeControl: false,
29272     disableDoubleClickZoom: false,
29273     scrollwheel: true,
29274     streetViewControl: false,
29275     radius: 0,
29276     locationName: '',
29277     draggable: true,
29278     enableAutocomplete: false,
29279     enableReverseGeocode: true,
29280     markerTitle: '',
29281     
29282     getAutoCreate: function()
29283     {
29284
29285         var cfg = {
29286             tag: 'div',
29287             cls: 'roo-location-picker'
29288         };
29289         
29290         return cfg
29291     },
29292     
29293     initEvents: function(ct, position)
29294     {       
29295         if(!this.el.getWidth() || this.isApplied()){
29296             return;
29297         }
29298         
29299         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29300         
29301         this.initial();
29302     },
29303     
29304     initial: function()
29305     {
29306         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29307             this.fireEvent('loadexception', this);
29308             return;
29309         }
29310         
29311         if(!this.mapTypeId){
29312             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29313         }
29314         
29315         this.gMapContext = this.GMapContext();
29316         
29317         this.initOverlayView();
29318         
29319         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29320         
29321         var _this = this;
29322                 
29323         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29324             _this.setPosition(_this.gMapContext.marker.position);
29325         });
29326         
29327         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29328             _this.fireEvent('mapClick', this, event);
29329             
29330         });
29331
29332         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29333             _this.fireEvent('mapRightClick', this, event);
29334             
29335         });
29336         
29337         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29338             _this.fireEvent('markerClick', this, event);
29339             
29340         });
29341
29342         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29343             _this.fireEvent('markerRightClick', this, event);
29344             
29345         });
29346         
29347         this.setPosition(this.gMapContext.location);
29348         
29349         this.fireEvent('initial', this, this.gMapContext.location);
29350     },
29351     
29352     initOverlayView: function()
29353     {
29354         var _this = this;
29355         
29356         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29357             
29358             draw: function()
29359             {
29360                 _this.fireEvent('OverlayViewDraw', _this);
29361             },
29362             
29363             onAdd: function()
29364             {
29365                 _this.fireEvent('OverlayViewOnAdd', _this);
29366             },
29367             
29368             onRemove: function()
29369             {
29370                 _this.fireEvent('OverlayViewOnRemove', _this);
29371             },
29372             
29373             show: function(cpx)
29374             {
29375                 _this.fireEvent('OverlayViewShow', _this, cpx);
29376             },
29377             
29378             hide: function()
29379             {
29380                 _this.fireEvent('OverlayViewHide', _this);
29381             }
29382             
29383         });
29384     },
29385     
29386     fromLatLngToContainerPixel: function(event)
29387     {
29388         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29389     },
29390     
29391     isApplied: function() 
29392     {
29393         return this.getGmapContext() == false ? false : true;
29394     },
29395     
29396     getGmapContext: function() 
29397     {
29398         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29399     },
29400     
29401     GMapContext: function() 
29402     {
29403         var position = new google.maps.LatLng(this.latitude, this.longitude);
29404         
29405         var _map = new google.maps.Map(this.el.dom, {
29406             center: position,
29407             zoom: this.zoom,
29408             mapTypeId: this.mapTypeId,
29409             mapTypeControl: this.mapTypeControl,
29410             disableDoubleClickZoom: this.disableDoubleClickZoom,
29411             scrollwheel: this.scrollwheel,
29412             streetViewControl: this.streetViewControl,
29413             locationName: this.locationName,
29414             draggable: this.draggable,
29415             enableAutocomplete: this.enableAutocomplete,
29416             enableReverseGeocode: this.enableReverseGeocode
29417         });
29418         
29419         var _marker = new google.maps.Marker({
29420             position: position,
29421             map: _map,
29422             title: this.markerTitle,
29423             draggable: this.draggable
29424         });
29425         
29426         return {
29427             map: _map,
29428             marker: _marker,
29429             circle: null,
29430             location: position,
29431             radius: this.radius,
29432             locationName: this.locationName,
29433             addressComponents: {
29434                 formatted_address: null,
29435                 addressLine1: null,
29436                 addressLine2: null,
29437                 streetName: null,
29438                 streetNumber: null,
29439                 city: null,
29440                 district: null,
29441                 state: null,
29442                 stateOrProvince: null
29443             },
29444             settings: this,
29445             domContainer: this.el.dom,
29446             geodecoder: new google.maps.Geocoder()
29447         };
29448     },
29449     
29450     drawCircle: function(center, radius, options) 
29451     {
29452         if (this.gMapContext.circle != null) {
29453             this.gMapContext.circle.setMap(null);
29454         }
29455         if (radius > 0) {
29456             radius *= 1;
29457             options = Roo.apply({}, options, {
29458                 strokeColor: "#0000FF",
29459                 strokeOpacity: .35,
29460                 strokeWeight: 2,
29461                 fillColor: "#0000FF",
29462                 fillOpacity: .2
29463             });
29464             
29465             options.map = this.gMapContext.map;
29466             options.radius = radius;
29467             options.center = center;
29468             this.gMapContext.circle = new google.maps.Circle(options);
29469             return this.gMapContext.circle;
29470         }
29471         
29472         return null;
29473     },
29474     
29475     setPosition: function(location) 
29476     {
29477         this.gMapContext.location = location;
29478         this.gMapContext.marker.setPosition(location);
29479         this.gMapContext.map.panTo(location);
29480         this.drawCircle(location, this.gMapContext.radius, {});
29481         
29482         var _this = this;
29483         
29484         if (this.gMapContext.settings.enableReverseGeocode) {
29485             this.gMapContext.geodecoder.geocode({
29486                 latLng: this.gMapContext.location
29487             }, function(results, status) {
29488                 
29489                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29490                     _this.gMapContext.locationName = results[0].formatted_address;
29491                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29492                     
29493                     _this.fireEvent('positionchanged', this, location);
29494                 }
29495             });
29496             
29497             return;
29498         }
29499         
29500         this.fireEvent('positionchanged', this, location);
29501     },
29502     
29503     resize: function()
29504     {
29505         google.maps.event.trigger(this.gMapContext.map, "resize");
29506         
29507         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29508         
29509         this.fireEvent('resize', this);
29510     },
29511     
29512     setPositionByLatLng: function(latitude, longitude)
29513     {
29514         this.setPosition(new google.maps.LatLng(latitude, longitude));
29515     },
29516     
29517     getCurrentPosition: function() 
29518     {
29519         return {
29520             latitude: this.gMapContext.location.lat(),
29521             longitude: this.gMapContext.location.lng()
29522         };
29523     },
29524     
29525     getAddressName: function() 
29526     {
29527         return this.gMapContext.locationName;
29528     },
29529     
29530     getAddressComponents: function() 
29531     {
29532         return this.gMapContext.addressComponents;
29533     },
29534     
29535     address_component_from_google_geocode: function(address_components) 
29536     {
29537         var result = {};
29538         
29539         for (var i = 0; i < address_components.length; i++) {
29540             var component = address_components[i];
29541             if (component.types.indexOf("postal_code") >= 0) {
29542                 result.postalCode = component.short_name;
29543             } else if (component.types.indexOf("street_number") >= 0) {
29544                 result.streetNumber = component.short_name;
29545             } else if (component.types.indexOf("route") >= 0) {
29546                 result.streetName = component.short_name;
29547             } else if (component.types.indexOf("neighborhood") >= 0) {
29548                 result.city = component.short_name;
29549             } else if (component.types.indexOf("locality") >= 0) {
29550                 result.city = component.short_name;
29551             } else if (component.types.indexOf("sublocality") >= 0) {
29552                 result.district = component.short_name;
29553             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29554                 result.stateOrProvince = component.short_name;
29555             } else if (component.types.indexOf("country") >= 0) {
29556                 result.country = component.short_name;
29557             }
29558         }
29559         
29560         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29561         result.addressLine2 = "";
29562         return result;
29563     },
29564     
29565     setZoomLevel: function(zoom)
29566     {
29567         this.gMapContext.map.setZoom(zoom);
29568     },
29569     
29570     show: function()
29571     {
29572         if(!this.el){
29573             return;
29574         }
29575         
29576         this.el.show();
29577         
29578         this.resize();
29579         
29580         this.fireEvent('show', this);
29581     },
29582     
29583     hide: function()
29584     {
29585         if(!this.el){
29586             return;
29587         }
29588         
29589         this.el.hide();
29590         
29591         this.fireEvent('hide', this);
29592     }
29593     
29594 });
29595
29596 Roo.apply(Roo.bootstrap.LocationPicker, {
29597     
29598     OverlayView : function(map, options)
29599     {
29600         options = options || {};
29601         
29602         this.setMap(map);
29603     }
29604     
29605     
29606 });/**
29607  * @class Roo.bootstrap.Alert
29608  * @extends Roo.bootstrap.Component
29609  * Bootstrap Alert class - shows an alert area box
29610  * eg
29611  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29612   Enter a valid email address
29613 </div>
29614  * @licence LGPL
29615  * @cfg {String} title The title of alert
29616  * @cfg {String} html The content of alert
29617  * @cfg {String} weight (  success | info | warning | danger )
29618  * @cfg {String} faicon font-awesomeicon
29619  * 
29620  * @constructor
29621  * Create a new alert
29622  * @param {Object} config The config object
29623  */
29624
29625
29626 Roo.bootstrap.Alert = function(config){
29627     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29628     
29629 };
29630
29631 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29632     
29633     title: '',
29634     html: '',
29635     weight: false,
29636     faicon: false,
29637     
29638     getAutoCreate : function()
29639     {
29640         
29641         var cfg = {
29642             tag : 'div',
29643             cls : 'alert',
29644             cn : [
29645                 {
29646                     tag : 'i',
29647                     cls : 'roo-alert-icon'
29648                     
29649                 },
29650                 {
29651                     tag : 'b',
29652                     cls : 'roo-alert-title',
29653                     html : this.title
29654                 },
29655                 {
29656                     tag : 'span',
29657                     cls : 'roo-alert-text',
29658                     html : this.html
29659                 }
29660             ]
29661         };
29662         
29663         if(this.faicon){
29664             cfg.cn[0].cls += ' fa ' + this.faicon;
29665         }
29666         
29667         if(this.weight){
29668             cfg.cls += ' alert-' + this.weight;
29669         }
29670         
29671         return cfg;
29672     },
29673     
29674     initEvents: function() 
29675     {
29676         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29677     },
29678     
29679     setTitle : function(str)
29680     {
29681         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29682     },
29683     
29684     setText : function(str)
29685     {
29686         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29687     },
29688     
29689     setWeight : function(weight)
29690     {
29691         if(this.weight){
29692             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29693         }
29694         
29695         this.weight = weight;
29696         
29697         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29698     },
29699     
29700     setIcon : function(icon)
29701     {
29702         if(this.faicon){
29703             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29704         }
29705         
29706         this.faicon = icon;
29707         
29708         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29709     },
29710     
29711     hide: function() 
29712     {
29713         this.el.hide();   
29714     },
29715     
29716     show: function() 
29717     {  
29718         this.el.show();   
29719     }
29720     
29721 });
29722
29723  
29724 /*
29725 * Licence: LGPL
29726 */
29727
29728 /**
29729  * @class Roo.bootstrap.UploadCropbox
29730  * @extends Roo.bootstrap.Component
29731  * Bootstrap UploadCropbox class
29732  * @cfg {String} emptyText show when image has been loaded
29733  * @cfg {String} rotateNotify show when image too small to rotate
29734  * @cfg {Number} errorTimeout default 3000
29735  * @cfg {Number} minWidth default 300
29736  * @cfg {Number} minHeight default 300
29737  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29738  * @cfg {Boolean} isDocument (true|false) default false
29739  * @cfg {String} url action url
29740  * @cfg {String} paramName default 'imageUpload'
29741  * @cfg {String} method default POST
29742  * @cfg {Boolean} loadMask (true|false) default true
29743  * @cfg {Boolean} loadingText default 'Loading...'
29744  * 
29745  * @constructor
29746  * Create a new UploadCropbox
29747  * @param {Object} config The config object
29748  */
29749
29750 Roo.bootstrap.UploadCropbox = function(config){
29751     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29752     
29753     this.addEvents({
29754         /**
29755          * @event beforeselectfile
29756          * Fire before select file
29757          * @param {Roo.bootstrap.UploadCropbox} this
29758          */
29759         "beforeselectfile" : true,
29760         /**
29761          * @event initial
29762          * Fire after initEvent
29763          * @param {Roo.bootstrap.UploadCropbox} this
29764          */
29765         "initial" : true,
29766         /**
29767          * @event crop
29768          * Fire after initEvent
29769          * @param {Roo.bootstrap.UploadCropbox} this
29770          * @param {String} data
29771          */
29772         "crop" : true,
29773         /**
29774          * @event prepare
29775          * Fire when preparing the file data
29776          * @param {Roo.bootstrap.UploadCropbox} this
29777          * @param {Object} file
29778          */
29779         "prepare" : true,
29780         /**
29781          * @event exception
29782          * Fire when get exception
29783          * @param {Roo.bootstrap.UploadCropbox} this
29784          * @param {XMLHttpRequest} xhr
29785          */
29786         "exception" : true,
29787         /**
29788          * @event beforeloadcanvas
29789          * Fire before load the canvas
29790          * @param {Roo.bootstrap.UploadCropbox} this
29791          * @param {String} src
29792          */
29793         "beforeloadcanvas" : true,
29794         /**
29795          * @event trash
29796          * Fire when trash image
29797          * @param {Roo.bootstrap.UploadCropbox} this
29798          */
29799         "trash" : true,
29800         /**
29801          * @event download
29802          * Fire when download the image
29803          * @param {Roo.bootstrap.UploadCropbox} this
29804          */
29805         "download" : true,
29806         /**
29807          * @event footerbuttonclick
29808          * Fire when footerbuttonclick
29809          * @param {Roo.bootstrap.UploadCropbox} this
29810          * @param {String} type
29811          */
29812         "footerbuttonclick" : true,
29813         /**
29814          * @event resize
29815          * Fire when resize
29816          * @param {Roo.bootstrap.UploadCropbox} this
29817          */
29818         "resize" : true,
29819         /**
29820          * @event rotate
29821          * Fire when rotate the image
29822          * @param {Roo.bootstrap.UploadCropbox} this
29823          * @param {String} pos
29824          */
29825         "rotate" : true,
29826         /**
29827          * @event inspect
29828          * Fire when inspect the file
29829          * @param {Roo.bootstrap.UploadCropbox} this
29830          * @param {Object} file
29831          */
29832         "inspect" : true,
29833         /**
29834          * @event upload
29835          * Fire when xhr upload the file
29836          * @param {Roo.bootstrap.UploadCropbox} this
29837          * @param {Object} data
29838          */
29839         "upload" : true,
29840         /**
29841          * @event arrange
29842          * Fire when arrange the file data
29843          * @param {Roo.bootstrap.UploadCropbox} this
29844          * @param {Object} formData
29845          */
29846         "arrange" : true
29847     });
29848     
29849     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29850 };
29851
29852 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29853     
29854     emptyText : 'Click to upload image',
29855     rotateNotify : 'Image is too small to rotate',
29856     errorTimeout : 3000,
29857     scale : 0,
29858     baseScale : 1,
29859     rotate : 0,
29860     dragable : false,
29861     pinching : false,
29862     mouseX : 0,
29863     mouseY : 0,
29864     cropData : false,
29865     minWidth : 300,
29866     minHeight : 300,
29867     file : false,
29868     exif : {},
29869     baseRotate : 1,
29870     cropType : 'image/jpeg',
29871     buttons : false,
29872     canvasLoaded : false,
29873     isDocument : false,
29874     method : 'POST',
29875     paramName : 'imageUpload',
29876     loadMask : true,
29877     loadingText : 'Loading...',
29878     maskEl : false,
29879     
29880     getAutoCreate : function()
29881     {
29882         var cfg = {
29883             tag : 'div',
29884             cls : 'roo-upload-cropbox',
29885             cn : [
29886                 {
29887                     tag : 'input',
29888                     cls : 'roo-upload-cropbox-selector',
29889                     type : 'file'
29890                 },
29891                 {
29892                     tag : 'div',
29893                     cls : 'roo-upload-cropbox-body',
29894                     style : 'cursor:pointer',
29895                     cn : [
29896                         {
29897                             tag : 'div',
29898                             cls : 'roo-upload-cropbox-preview'
29899                         },
29900                         {
29901                             tag : 'div',
29902                             cls : 'roo-upload-cropbox-thumb'
29903                         },
29904                         {
29905                             tag : 'div',
29906                             cls : 'roo-upload-cropbox-empty-notify',
29907                             html : this.emptyText
29908                         },
29909                         {
29910                             tag : 'div',
29911                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29912                             html : this.rotateNotify
29913                         }
29914                     ]
29915                 },
29916                 {
29917                     tag : 'div',
29918                     cls : 'roo-upload-cropbox-footer',
29919                     cn : {
29920                         tag : 'div',
29921                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29922                         cn : []
29923                     }
29924                 }
29925             ]
29926         };
29927         
29928         return cfg;
29929     },
29930     
29931     onRender : function(ct, position)
29932     {
29933         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29934         
29935         if (this.buttons.length) {
29936             
29937             Roo.each(this.buttons, function(bb) {
29938                 
29939                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29940                 
29941                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29942                 
29943             }, this);
29944         }
29945         
29946         if(this.loadMask){
29947             this.maskEl = this.el;
29948         }
29949     },
29950     
29951     initEvents : function()
29952     {
29953         this.urlAPI = (window.createObjectURL && window) || 
29954                                 (window.URL && URL.revokeObjectURL && URL) || 
29955                                 (window.webkitURL && webkitURL);
29956                         
29957         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29958         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29959         
29960         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29961         this.selectorEl.hide();
29962         
29963         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29964         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29965         
29966         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29967         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29968         this.thumbEl.hide();
29969         
29970         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29971         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29972         
29973         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29974         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29975         this.errorEl.hide();
29976         
29977         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29978         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29979         this.footerEl.hide();
29980         
29981         this.setThumbBoxSize();
29982         
29983         this.bind();
29984         
29985         this.resize();
29986         
29987         this.fireEvent('initial', this);
29988     },
29989
29990     bind : function()
29991     {
29992         var _this = this;
29993         
29994         window.addEventListener("resize", function() { _this.resize(); } );
29995         
29996         this.bodyEl.on('click', this.beforeSelectFile, this);
29997         
29998         if(Roo.isTouch){
29999             this.bodyEl.on('touchstart', this.onTouchStart, this);
30000             this.bodyEl.on('touchmove', this.onTouchMove, this);
30001             this.bodyEl.on('touchend', this.onTouchEnd, this);
30002         }
30003         
30004         if(!Roo.isTouch){
30005             this.bodyEl.on('mousedown', this.onMouseDown, this);
30006             this.bodyEl.on('mousemove', this.onMouseMove, this);
30007             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30008             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30009             Roo.get(document).on('mouseup', this.onMouseUp, this);
30010         }
30011         
30012         this.selectorEl.on('change', this.onFileSelected, this);
30013     },
30014     
30015     reset : function()
30016     {    
30017         this.scale = 0;
30018         this.baseScale = 1;
30019         this.rotate = 0;
30020         this.baseRotate = 1;
30021         this.dragable = false;
30022         this.pinching = false;
30023         this.mouseX = 0;
30024         this.mouseY = 0;
30025         this.cropData = false;
30026         this.notifyEl.dom.innerHTML = this.emptyText;
30027         
30028         this.selectorEl.dom.value = '';
30029         
30030     },
30031     
30032     resize : function()
30033     {
30034         if(this.fireEvent('resize', this) != false){
30035             this.setThumbBoxPosition();
30036             this.setCanvasPosition();
30037         }
30038     },
30039     
30040     onFooterButtonClick : function(e, el, o, type)
30041     {
30042         switch (type) {
30043             case 'rotate-left' :
30044                 this.onRotateLeft(e);
30045                 break;
30046             case 'rotate-right' :
30047                 this.onRotateRight(e);
30048                 break;
30049             case 'picture' :
30050                 this.beforeSelectFile(e);
30051                 break;
30052             case 'trash' :
30053                 this.trash(e);
30054                 break;
30055             case 'crop' :
30056                 this.crop(e);
30057                 break;
30058             case 'download' :
30059                 this.download(e);
30060                 break;
30061             default :
30062                 break;
30063         }
30064         
30065         this.fireEvent('footerbuttonclick', this, type);
30066     },
30067     
30068     beforeSelectFile : function(e)
30069     {
30070         e.preventDefault();
30071         
30072         if(this.fireEvent('beforeselectfile', this) != false){
30073             this.selectorEl.dom.click();
30074         }
30075     },
30076     
30077     onFileSelected : function(e)
30078     {
30079         e.preventDefault();
30080         
30081         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30082             return;
30083         }
30084         
30085         var file = this.selectorEl.dom.files[0];
30086         
30087         if(this.fireEvent('inspect', this, file) != false){
30088             this.prepare(file);
30089         }
30090         
30091     },
30092     
30093     trash : function(e)
30094     {
30095         this.fireEvent('trash', this);
30096     },
30097     
30098     download : function(e)
30099     {
30100         this.fireEvent('download', this);
30101     },
30102     
30103     loadCanvas : function(src)
30104     {   
30105         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30106             
30107             this.reset();
30108             
30109             this.imageEl = document.createElement('img');
30110             
30111             var _this = this;
30112             
30113             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30114             
30115             this.imageEl.src = src;
30116         }
30117     },
30118     
30119     onLoadCanvas : function()
30120     {   
30121         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30122         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30123         
30124         this.bodyEl.un('click', this.beforeSelectFile, this);
30125         
30126         this.notifyEl.hide();
30127         this.thumbEl.show();
30128         this.footerEl.show();
30129         
30130         this.baseRotateLevel();
30131         
30132         if(this.isDocument){
30133             this.setThumbBoxSize();
30134         }
30135         
30136         this.setThumbBoxPosition();
30137         
30138         this.baseScaleLevel();
30139         
30140         this.draw();
30141         
30142         this.resize();
30143         
30144         this.canvasLoaded = true;
30145         
30146         if(this.loadMask){
30147             this.maskEl.unmask();
30148         }
30149         
30150     },
30151     
30152     setCanvasPosition : function()
30153     {   
30154         if(!this.canvasEl){
30155             return;
30156         }
30157         
30158         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30159         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30160         
30161         this.previewEl.setLeft(pw);
30162         this.previewEl.setTop(ph);
30163         
30164     },
30165     
30166     onMouseDown : function(e)
30167     {   
30168         e.stopEvent();
30169         
30170         this.dragable = true;
30171         this.pinching = false;
30172         
30173         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30174             this.dragable = false;
30175             return;
30176         }
30177         
30178         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30179         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30180         
30181     },
30182     
30183     onMouseMove : function(e)
30184     {   
30185         e.stopEvent();
30186         
30187         if(!this.canvasLoaded){
30188             return;
30189         }
30190         
30191         if (!this.dragable){
30192             return;
30193         }
30194         
30195         var minX = Math.ceil(this.thumbEl.getLeft(true));
30196         var minY = Math.ceil(this.thumbEl.getTop(true));
30197         
30198         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30199         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30200         
30201         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30202         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30203         
30204         x = x - this.mouseX;
30205         y = y - this.mouseY;
30206         
30207         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30208         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30209         
30210         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30211         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30212         
30213         this.previewEl.setLeft(bgX);
30214         this.previewEl.setTop(bgY);
30215         
30216         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30217         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30218     },
30219     
30220     onMouseUp : function(e)
30221     {   
30222         e.stopEvent();
30223         
30224         this.dragable = false;
30225     },
30226     
30227     onMouseWheel : function(e)
30228     {   
30229         e.stopEvent();
30230         
30231         this.startScale = this.scale;
30232         
30233         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30234         
30235         if(!this.zoomable()){
30236             this.scale = this.startScale;
30237             return;
30238         }
30239         
30240         this.draw();
30241         
30242         return;
30243     },
30244     
30245     zoomable : function()
30246     {
30247         var minScale = this.thumbEl.getWidth() / this.minWidth;
30248         
30249         if(this.minWidth < this.minHeight){
30250             minScale = this.thumbEl.getHeight() / this.minHeight;
30251         }
30252         
30253         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30254         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30255         
30256         if(
30257                 this.isDocument &&
30258                 (this.rotate == 0 || this.rotate == 180) && 
30259                 (
30260                     width > this.imageEl.OriginWidth || 
30261                     height > this.imageEl.OriginHeight ||
30262                     (width < this.minWidth && height < this.minHeight)
30263                 )
30264         ){
30265             return false;
30266         }
30267         
30268         if(
30269                 this.isDocument &&
30270                 (this.rotate == 90 || this.rotate == 270) && 
30271                 (
30272                     width > this.imageEl.OriginWidth || 
30273                     height > this.imageEl.OriginHeight ||
30274                     (width < this.minHeight && height < this.minWidth)
30275                 )
30276         ){
30277             return false;
30278         }
30279         
30280         if(
30281                 !this.isDocument &&
30282                 (this.rotate == 0 || this.rotate == 180) && 
30283                 (
30284                     width < this.minWidth || 
30285                     width > this.imageEl.OriginWidth || 
30286                     height < this.minHeight || 
30287                     height > this.imageEl.OriginHeight
30288                 )
30289         ){
30290             return false;
30291         }
30292         
30293         if(
30294                 !this.isDocument &&
30295                 (this.rotate == 90 || this.rotate == 270) && 
30296                 (
30297                     width < this.minHeight || 
30298                     width > this.imageEl.OriginWidth || 
30299                     height < this.minWidth || 
30300                     height > this.imageEl.OriginHeight
30301                 )
30302         ){
30303             return false;
30304         }
30305         
30306         return true;
30307         
30308     },
30309     
30310     onRotateLeft : function(e)
30311     {   
30312         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30313             
30314             var minScale = this.thumbEl.getWidth() / this.minWidth;
30315             
30316             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30317             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30318             
30319             this.startScale = this.scale;
30320             
30321             while (this.getScaleLevel() < minScale){
30322             
30323                 this.scale = this.scale + 1;
30324                 
30325                 if(!this.zoomable()){
30326                     break;
30327                 }
30328                 
30329                 if(
30330                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30331                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30332                 ){
30333                     continue;
30334                 }
30335                 
30336                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30337
30338                 this.draw();
30339                 
30340                 return;
30341             }
30342             
30343             this.scale = this.startScale;
30344             
30345             this.onRotateFail();
30346             
30347             return false;
30348         }
30349         
30350         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30351
30352         if(this.isDocument){
30353             this.setThumbBoxSize();
30354             this.setThumbBoxPosition();
30355             this.setCanvasPosition();
30356         }
30357         
30358         this.draw();
30359         
30360         this.fireEvent('rotate', this, 'left');
30361         
30362     },
30363     
30364     onRotateRight : function(e)
30365     {
30366         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30367             
30368             var minScale = this.thumbEl.getWidth() / this.minWidth;
30369         
30370             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30371             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30372             
30373             this.startScale = this.scale;
30374             
30375             while (this.getScaleLevel() < minScale){
30376             
30377                 this.scale = this.scale + 1;
30378                 
30379                 if(!this.zoomable()){
30380                     break;
30381                 }
30382                 
30383                 if(
30384                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30385                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30386                 ){
30387                     continue;
30388                 }
30389                 
30390                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30391
30392                 this.draw();
30393                 
30394                 return;
30395             }
30396             
30397             this.scale = this.startScale;
30398             
30399             this.onRotateFail();
30400             
30401             return false;
30402         }
30403         
30404         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30405
30406         if(this.isDocument){
30407             this.setThumbBoxSize();
30408             this.setThumbBoxPosition();
30409             this.setCanvasPosition();
30410         }
30411         
30412         this.draw();
30413         
30414         this.fireEvent('rotate', this, 'right');
30415     },
30416     
30417     onRotateFail : function()
30418     {
30419         this.errorEl.show(true);
30420         
30421         var _this = this;
30422         
30423         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30424     },
30425     
30426     draw : function()
30427     {
30428         this.previewEl.dom.innerHTML = '';
30429         
30430         var canvasEl = document.createElement("canvas");
30431         
30432         var contextEl = canvasEl.getContext("2d");
30433         
30434         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30435         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30436         var center = this.imageEl.OriginWidth / 2;
30437         
30438         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30439             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30440             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30441             center = this.imageEl.OriginHeight / 2;
30442         }
30443         
30444         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30445         
30446         contextEl.translate(center, center);
30447         contextEl.rotate(this.rotate * Math.PI / 180);
30448
30449         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30450         
30451         this.canvasEl = document.createElement("canvas");
30452         
30453         this.contextEl = this.canvasEl.getContext("2d");
30454         
30455         switch (this.rotate) {
30456             case 0 :
30457                 
30458                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30459                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30460                 
30461                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30462                 
30463                 break;
30464             case 90 : 
30465                 
30466                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30467                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30468                 
30469                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30470                     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);
30471                     break;
30472                 }
30473                 
30474                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30475                 
30476                 break;
30477             case 180 :
30478                 
30479                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30480                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30481                 
30482                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30483                     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);
30484                     break;
30485                 }
30486                 
30487                 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);
30488                 
30489                 break;
30490             case 270 :
30491                 
30492                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30493                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30494         
30495                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30496                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30497                     break;
30498                 }
30499                 
30500                 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);
30501                 
30502                 break;
30503             default : 
30504                 break;
30505         }
30506         
30507         this.previewEl.appendChild(this.canvasEl);
30508         
30509         this.setCanvasPosition();
30510     },
30511     
30512     crop : function()
30513     {
30514         if(!this.canvasLoaded){
30515             return;
30516         }
30517         
30518         var imageCanvas = document.createElement("canvas");
30519         
30520         var imageContext = imageCanvas.getContext("2d");
30521         
30522         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30523         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30524         
30525         var center = imageCanvas.width / 2;
30526         
30527         imageContext.translate(center, center);
30528         
30529         imageContext.rotate(this.rotate * Math.PI / 180);
30530         
30531         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30532         
30533         var canvas = document.createElement("canvas");
30534         
30535         var context = canvas.getContext("2d");
30536                 
30537         canvas.width = this.minWidth;
30538         canvas.height = this.minHeight;
30539
30540         switch (this.rotate) {
30541             case 0 :
30542                 
30543                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30544                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30545                 
30546                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30547                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30548                 
30549                 var targetWidth = this.minWidth - 2 * x;
30550                 var targetHeight = this.minHeight - 2 * y;
30551                 
30552                 var scale = 1;
30553                 
30554                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30555                     scale = targetWidth / width;
30556                 }
30557                 
30558                 if(x > 0 && y == 0){
30559                     scale = targetHeight / height;
30560                 }
30561                 
30562                 if(x > 0 && y > 0){
30563                     scale = targetWidth / width;
30564                     
30565                     if(width < height){
30566                         scale = targetHeight / height;
30567                     }
30568                 }
30569                 
30570                 context.scale(scale, scale);
30571                 
30572                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30573                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30574
30575                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30576                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30577
30578                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30579                 
30580                 break;
30581             case 90 : 
30582                 
30583                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30584                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30585                 
30586                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30587                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30588                 
30589                 var targetWidth = this.minWidth - 2 * x;
30590                 var targetHeight = this.minHeight - 2 * y;
30591                 
30592                 var scale = 1;
30593                 
30594                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30595                     scale = targetWidth / width;
30596                 }
30597                 
30598                 if(x > 0 && y == 0){
30599                     scale = targetHeight / height;
30600                 }
30601                 
30602                 if(x > 0 && y > 0){
30603                     scale = targetWidth / width;
30604                     
30605                     if(width < height){
30606                         scale = targetHeight / height;
30607                     }
30608                 }
30609                 
30610                 context.scale(scale, scale);
30611                 
30612                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30613                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30614
30615                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30616                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30617                 
30618                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30619                 
30620                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30621                 
30622                 break;
30623             case 180 :
30624                 
30625                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30626                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30627                 
30628                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30629                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30630                 
30631                 var targetWidth = this.minWidth - 2 * x;
30632                 var targetHeight = this.minHeight - 2 * y;
30633                 
30634                 var scale = 1;
30635                 
30636                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30637                     scale = targetWidth / width;
30638                 }
30639                 
30640                 if(x > 0 && y == 0){
30641                     scale = targetHeight / height;
30642                 }
30643                 
30644                 if(x > 0 && y > 0){
30645                     scale = targetWidth / width;
30646                     
30647                     if(width < height){
30648                         scale = targetHeight / height;
30649                     }
30650                 }
30651                 
30652                 context.scale(scale, scale);
30653                 
30654                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30655                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30656
30657                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30658                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30659
30660                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30661                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30662                 
30663                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30664                 
30665                 break;
30666             case 270 :
30667                 
30668                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30669                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30670                 
30671                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30672                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30673                 
30674                 var targetWidth = this.minWidth - 2 * x;
30675                 var targetHeight = this.minHeight - 2 * y;
30676                 
30677                 var scale = 1;
30678                 
30679                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30680                     scale = targetWidth / width;
30681                 }
30682                 
30683                 if(x > 0 && y == 0){
30684                     scale = targetHeight / height;
30685                 }
30686                 
30687                 if(x > 0 && y > 0){
30688                     scale = targetWidth / width;
30689                     
30690                     if(width < height){
30691                         scale = targetHeight / height;
30692                     }
30693                 }
30694                 
30695                 context.scale(scale, scale);
30696                 
30697                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30698                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30699
30700                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30701                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30702                 
30703                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30704                 
30705                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30706                 
30707                 break;
30708             default : 
30709                 break;
30710         }
30711         
30712         this.cropData = canvas.toDataURL(this.cropType);
30713         
30714         if(this.fireEvent('crop', this, this.cropData) !== false){
30715             this.process(this.file, this.cropData);
30716         }
30717         
30718         return;
30719         
30720     },
30721     
30722     setThumbBoxSize : function()
30723     {
30724         var width, height;
30725         
30726         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30727             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30728             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30729             
30730             this.minWidth = width;
30731             this.minHeight = height;
30732             
30733             if(this.rotate == 90 || this.rotate == 270){
30734                 this.minWidth = height;
30735                 this.minHeight = width;
30736             }
30737         }
30738         
30739         height = 300;
30740         width = Math.ceil(this.minWidth * height / this.minHeight);
30741         
30742         if(this.minWidth > this.minHeight){
30743             width = 300;
30744             height = Math.ceil(this.minHeight * width / this.minWidth);
30745         }
30746         
30747         this.thumbEl.setStyle({
30748             width : width + 'px',
30749             height : height + 'px'
30750         });
30751
30752         return;
30753             
30754     },
30755     
30756     setThumbBoxPosition : function()
30757     {
30758         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30759         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30760         
30761         this.thumbEl.setLeft(x);
30762         this.thumbEl.setTop(y);
30763         
30764     },
30765     
30766     baseRotateLevel : function()
30767     {
30768         this.baseRotate = 1;
30769         
30770         if(
30771                 typeof(this.exif) != 'undefined' &&
30772                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30773                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30774         ){
30775             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30776         }
30777         
30778         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30779         
30780     },
30781     
30782     baseScaleLevel : function()
30783     {
30784         var width, height;
30785         
30786         if(this.isDocument){
30787             
30788             if(this.baseRotate == 6 || this.baseRotate == 8){
30789             
30790                 height = this.thumbEl.getHeight();
30791                 this.baseScale = height / this.imageEl.OriginWidth;
30792
30793                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30794                     width = this.thumbEl.getWidth();
30795                     this.baseScale = width / this.imageEl.OriginHeight;
30796                 }
30797
30798                 return;
30799             }
30800
30801             height = this.thumbEl.getHeight();
30802             this.baseScale = height / this.imageEl.OriginHeight;
30803
30804             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30805                 width = this.thumbEl.getWidth();
30806                 this.baseScale = width / this.imageEl.OriginWidth;
30807             }
30808
30809             return;
30810         }
30811         
30812         if(this.baseRotate == 6 || this.baseRotate == 8){
30813             
30814             width = this.thumbEl.getHeight();
30815             this.baseScale = width / this.imageEl.OriginHeight;
30816             
30817             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30818                 height = this.thumbEl.getWidth();
30819                 this.baseScale = height / this.imageEl.OriginHeight;
30820             }
30821             
30822             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30823                 height = this.thumbEl.getWidth();
30824                 this.baseScale = height / this.imageEl.OriginHeight;
30825                 
30826                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30827                     width = this.thumbEl.getHeight();
30828                     this.baseScale = width / this.imageEl.OriginWidth;
30829                 }
30830             }
30831             
30832             return;
30833         }
30834         
30835         width = this.thumbEl.getWidth();
30836         this.baseScale = width / this.imageEl.OriginWidth;
30837         
30838         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30839             height = this.thumbEl.getHeight();
30840             this.baseScale = height / this.imageEl.OriginHeight;
30841         }
30842         
30843         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30844             
30845             height = this.thumbEl.getHeight();
30846             this.baseScale = height / this.imageEl.OriginHeight;
30847             
30848             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30849                 width = this.thumbEl.getWidth();
30850                 this.baseScale = width / this.imageEl.OriginWidth;
30851             }
30852             
30853         }
30854         
30855         return;
30856     },
30857     
30858     getScaleLevel : function()
30859     {
30860         return this.baseScale * Math.pow(1.1, this.scale);
30861     },
30862     
30863     onTouchStart : function(e)
30864     {
30865         if(!this.canvasLoaded){
30866             this.beforeSelectFile(e);
30867             return;
30868         }
30869         
30870         var touches = e.browserEvent.touches;
30871         
30872         if(!touches){
30873             return;
30874         }
30875         
30876         if(touches.length == 1){
30877             this.onMouseDown(e);
30878             return;
30879         }
30880         
30881         if(touches.length != 2){
30882             return;
30883         }
30884         
30885         var coords = [];
30886         
30887         for(var i = 0, finger; finger = touches[i]; i++){
30888             coords.push(finger.pageX, finger.pageY);
30889         }
30890         
30891         var x = Math.pow(coords[0] - coords[2], 2);
30892         var y = Math.pow(coords[1] - coords[3], 2);
30893         
30894         this.startDistance = Math.sqrt(x + y);
30895         
30896         this.startScale = this.scale;
30897         
30898         this.pinching = true;
30899         this.dragable = false;
30900         
30901     },
30902     
30903     onTouchMove : function(e)
30904     {
30905         if(!this.pinching && !this.dragable){
30906             return;
30907         }
30908         
30909         var touches = e.browserEvent.touches;
30910         
30911         if(!touches){
30912             return;
30913         }
30914         
30915         if(this.dragable){
30916             this.onMouseMove(e);
30917             return;
30918         }
30919         
30920         var coords = [];
30921         
30922         for(var i = 0, finger; finger = touches[i]; i++){
30923             coords.push(finger.pageX, finger.pageY);
30924         }
30925         
30926         var x = Math.pow(coords[0] - coords[2], 2);
30927         var y = Math.pow(coords[1] - coords[3], 2);
30928         
30929         this.endDistance = Math.sqrt(x + y);
30930         
30931         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30932         
30933         if(!this.zoomable()){
30934             this.scale = this.startScale;
30935             return;
30936         }
30937         
30938         this.draw();
30939         
30940     },
30941     
30942     onTouchEnd : function(e)
30943     {
30944         this.pinching = false;
30945         this.dragable = false;
30946         
30947     },
30948     
30949     process : function(file, crop)
30950     {
30951         if(this.loadMask){
30952             this.maskEl.mask(this.loadingText);
30953         }
30954         
30955         this.xhr = new XMLHttpRequest();
30956         
30957         file.xhr = this.xhr;
30958
30959         this.xhr.open(this.method, this.url, true);
30960         
30961         var headers = {
30962             "Accept": "application/json",
30963             "Cache-Control": "no-cache",
30964             "X-Requested-With": "XMLHttpRequest"
30965         };
30966         
30967         for (var headerName in headers) {
30968             var headerValue = headers[headerName];
30969             if (headerValue) {
30970                 this.xhr.setRequestHeader(headerName, headerValue);
30971             }
30972         }
30973         
30974         var _this = this;
30975         
30976         this.xhr.onload = function()
30977         {
30978             _this.xhrOnLoad(_this.xhr);
30979         }
30980         
30981         this.xhr.onerror = function()
30982         {
30983             _this.xhrOnError(_this.xhr);
30984         }
30985         
30986         var formData = new FormData();
30987
30988         formData.append('returnHTML', 'NO');
30989         
30990         if(crop){
30991             formData.append('crop', crop);
30992         }
30993         
30994         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30995             formData.append(this.paramName, file, file.name);
30996         }
30997         
30998         if(typeof(file.filename) != 'undefined'){
30999             formData.append('filename', file.filename);
31000         }
31001         
31002         if(typeof(file.mimetype) != 'undefined'){
31003             formData.append('mimetype', file.mimetype);
31004         }
31005         
31006         if(this.fireEvent('arrange', this, formData) != false){
31007             this.xhr.send(formData);
31008         };
31009     },
31010     
31011     xhrOnLoad : function(xhr)
31012     {
31013         if(this.loadMask){
31014             this.maskEl.unmask();
31015         }
31016         
31017         if (xhr.readyState !== 4) {
31018             this.fireEvent('exception', this, xhr);
31019             return;
31020         }
31021
31022         var response = Roo.decode(xhr.responseText);
31023         
31024         if(!response.success){
31025             this.fireEvent('exception', this, xhr);
31026             return;
31027         }
31028         
31029         var response = Roo.decode(xhr.responseText);
31030         
31031         this.fireEvent('upload', this, response);
31032         
31033     },
31034     
31035     xhrOnError : function()
31036     {
31037         if(this.loadMask){
31038             this.maskEl.unmask();
31039         }
31040         
31041         Roo.log('xhr on error');
31042         
31043         var response = Roo.decode(xhr.responseText);
31044           
31045         Roo.log(response);
31046         
31047     },
31048     
31049     prepare : function(file)
31050     {   
31051         if(this.loadMask){
31052             this.maskEl.mask(this.loadingText);
31053         }
31054         
31055         this.file = false;
31056         this.exif = {};
31057         
31058         if(typeof(file) === 'string'){
31059             this.loadCanvas(file);
31060             return;
31061         }
31062         
31063         if(!file || !this.urlAPI){
31064             return;
31065         }
31066         
31067         this.file = file;
31068         this.cropType = file.type;
31069         
31070         var _this = this;
31071         
31072         if(this.fireEvent('prepare', this, this.file) != false){
31073             
31074             var reader = new FileReader();
31075             
31076             reader.onload = function (e) {
31077                 if (e.target.error) {
31078                     Roo.log(e.target.error);
31079                     return;
31080                 }
31081                 
31082                 var buffer = e.target.result,
31083                     dataView = new DataView(buffer),
31084                     offset = 2,
31085                     maxOffset = dataView.byteLength - 4,
31086                     markerBytes,
31087                     markerLength;
31088                 
31089                 if (dataView.getUint16(0) === 0xffd8) {
31090                     while (offset < maxOffset) {
31091                         markerBytes = dataView.getUint16(offset);
31092                         
31093                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31094                             markerLength = dataView.getUint16(offset + 2) + 2;
31095                             if (offset + markerLength > dataView.byteLength) {
31096                                 Roo.log('Invalid meta data: Invalid segment size.');
31097                                 break;
31098                             }
31099                             
31100                             if(markerBytes == 0xffe1){
31101                                 _this.parseExifData(
31102                                     dataView,
31103                                     offset,
31104                                     markerLength
31105                                 );
31106                             }
31107                             
31108                             offset += markerLength;
31109                             
31110                             continue;
31111                         }
31112                         
31113                         break;
31114                     }
31115                     
31116                 }
31117                 
31118                 var url = _this.urlAPI.createObjectURL(_this.file);
31119                 
31120                 _this.loadCanvas(url);
31121                 
31122                 return;
31123             }
31124             
31125             reader.readAsArrayBuffer(this.file);
31126             
31127         }
31128         
31129     },
31130     
31131     parseExifData : function(dataView, offset, length)
31132     {
31133         var tiffOffset = offset + 10,
31134             littleEndian,
31135             dirOffset;
31136     
31137         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31138             // No Exif data, might be XMP data instead
31139             return;
31140         }
31141         
31142         // Check for the ASCII code for "Exif" (0x45786966):
31143         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31144             // No Exif data, might be XMP data instead
31145             return;
31146         }
31147         if (tiffOffset + 8 > dataView.byteLength) {
31148             Roo.log('Invalid Exif data: Invalid segment size.');
31149             return;
31150         }
31151         // Check for the two null bytes:
31152         if (dataView.getUint16(offset + 8) !== 0x0000) {
31153             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31154             return;
31155         }
31156         // Check the byte alignment:
31157         switch (dataView.getUint16(tiffOffset)) {
31158         case 0x4949:
31159             littleEndian = true;
31160             break;
31161         case 0x4D4D:
31162             littleEndian = false;
31163             break;
31164         default:
31165             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31166             return;
31167         }
31168         // Check for the TIFF tag marker (0x002A):
31169         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31170             Roo.log('Invalid Exif data: Missing TIFF marker.');
31171             return;
31172         }
31173         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31174         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31175         
31176         this.parseExifTags(
31177             dataView,
31178             tiffOffset,
31179             tiffOffset + dirOffset,
31180             littleEndian
31181         );
31182     },
31183     
31184     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31185     {
31186         var tagsNumber,
31187             dirEndOffset,
31188             i;
31189         if (dirOffset + 6 > dataView.byteLength) {
31190             Roo.log('Invalid Exif data: Invalid directory offset.');
31191             return;
31192         }
31193         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31194         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31195         if (dirEndOffset + 4 > dataView.byteLength) {
31196             Roo.log('Invalid Exif data: Invalid directory size.');
31197             return;
31198         }
31199         for (i = 0; i < tagsNumber; i += 1) {
31200             this.parseExifTag(
31201                 dataView,
31202                 tiffOffset,
31203                 dirOffset + 2 + 12 * i, // tag offset
31204                 littleEndian
31205             );
31206         }
31207         // Return the offset to the next directory:
31208         return dataView.getUint32(dirEndOffset, littleEndian);
31209     },
31210     
31211     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31212     {
31213         var tag = dataView.getUint16(offset, littleEndian);
31214         
31215         this.exif[tag] = this.getExifValue(
31216             dataView,
31217             tiffOffset,
31218             offset,
31219             dataView.getUint16(offset + 2, littleEndian), // tag type
31220             dataView.getUint32(offset + 4, littleEndian), // tag length
31221             littleEndian
31222         );
31223     },
31224     
31225     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31226     {
31227         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31228             tagSize,
31229             dataOffset,
31230             values,
31231             i,
31232             str,
31233             c;
31234     
31235         if (!tagType) {
31236             Roo.log('Invalid Exif data: Invalid tag type.');
31237             return;
31238         }
31239         
31240         tagSize = tagType.size * length;
31241         // Determine if the value is contained in the dataOffset bytes,
31242         // or if the value at the dataOffset is a pointer to the actual data:
31243         dataOffset = tagSize > 4 ?
31244                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31245         if (dataOffset + tagSize > dataView.byteLength) {
31246             Roo.log('Invalid Exif data: Invalid data offset.');
31247             return;
31248         }
31249         if (length === 1) {
31250             return tagType.getValue(dataView, dataOffset, littleEndian);
31251         }
31252         values = [];
31253         for (i = 0; i < length; i += 1) {
31254             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31255         }
31256         
31257         if (tagType.ascii) {
31258             str = '';
31259             // Concatenate the chars:
31260             for (i = 0; i < values.length; i += 1) {
31261                 c = values[i];
31262                 // Ignore the terminating NULL byte(s):
31263                 if (c === '\u0000') {
31264                     break;
31265                 }
31266                 str += c;
31267             }
31268             return str;
31269         }
31270         return values;
31271     }
31272     
31273 });
31274
31275 Roo.apply(Roo.bootstrap.UploadCropbox, {
31276     tags : {
31277         'Orientation': 0x0112
31278     },
31279     
31280     Orientation: {
31281             1: 0, //'top-left',
31282 //            2: 'top-right',
31283             3: 180, //'bottom-right',
31284 //            4: 'bottom-left',
31285 //            5: 'left-top',
31286             6: 90, //'right-top',
31287 //            7: 'right-bottom',
31288             8: 270 //'left-bottom'
31289     },
31290     
31291     exifTagTypes : {
31292         // byte, 8-bit unsigned int:
31293         1: {
31294             getValue: function (dataView, dataOffset) {
31295                 return dataView.getUint8(dataOffset);
31296             },
31297             size: 1
31298         },
31299         // ascii, 8-bit byte:
31300         2: {
31301             getValue: function (dataView, dataOffset) {
31302                 return String.fromCharCode(dataView.getUint8(dataOffset));
31303             },
31304             size: 1,
31305             ascii: true
31306         },
31307         // short, 16 bit int:
31308         3: {
31309             getValue: function (dataView, dataOffset, littleEndian) {
31310                 return dataView.getUint16(dataOffset, littleEndian);
31311             },
31312             size: 2
31313         },
31314         // long, 32 bit int:
31315         4: {
31316             getValue: function (dataView, dataOffset, littleEndian) {
31317                 return dataView.getUint32(dataOffset, littleEndian);
31318             },
31319             size: 4
31320         },
31321         // rational = two long values, first is numerator, second is denominator:
31322         5: {
31323             getValue: function (dataView, dataOffset, littleEndian) {
31324                 return dataView.getUint32(dataOffset, littleEndian) /
31325                     dataView.getUint32(dataOffset + 4, littleEndian);
31326             },
31327             size: 8
31328         },
31329         // slong, 32 bit signed int:
31330         9: {
31331             getValue: function (dataView, dataOffset, littleEndian) {
31332                 return dataView.getInt32(dataOffset, littleEndian);
31333             },
31334             size: 4
31335         },
31336         // srational, two slongs, first is numerator, second is denominator:
31337         10: {
31338             getValue: function (dataView, dataOffset, littleEndian) {
31339                 return dataView.getInt32(dataOffset, littleEndian) /
31340                     dataView.getInt32(dataOffset + 4, littleEndian);
31341             },
31342             size: 8
31343         }
31344     },
31345     
31346     footer : {
31347         STANDARD : [
31348             {
31349                 tag : 'div',
31350                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31351                 action : 'rotate-left',
31352                 cn : [
31353                     {
31354                         tag : 'button',
31355                         cls : 'btn btn-default',
31356                         html : '<i class="fa fa-undo"></i>'
31357                     }
31358                 ]
31359             },
31360             {
31361                 tag : 'div',
31362                 cls : 'btn-group roo-upload-cropbox-picture',
31363                 action : 'picture',
31364                 cn : [
31365                     {
31366                         tag : 'button',
31367                         cls : 'btn btn-default',
31368                         html : '<i class="fa fa-picture-o"></i>'
31369                     }
31370                 ]
31371             },
31372             {
31373                 tag : 'div',
31374                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31375                 action : 'rotate-right',
31376                 cn : [
31377                     {
31378                         tag : 'button',
31379                         cls : 'btn btn-default',
31380                         html : '<i class="fa fa-repeat"></i>'
31381                     }
31382                 ]
31383             }
31384         ],
31385         DOCUMENT : [
31386             {
31387                 tag : 'div',
31388                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31389                 action : 'rotate-left',
31390                 cn : [
31391                     {
31392                         tag : 'button',
31393                         cls : 'btn btn-default',
31394                         html : '<i class="fa fa-undo"></i>'
31395                     }
31396                 ]
31397             },
31398             {
31399                 tag : 'div',
31400                 cls : 'btn-group roo-upload-cropbox-download',
31401                 action : 'download',
31402                 cn : [
31403                     {
31404                         tag : 'button',
31405                         cls : 'btn btn-default',
31406                         html : '<i class="fa fa-download"></i>'
31407                     }
31408                 ]
31409             },
31410             {
31411                 tag : 'div',
31412                 cls : 'btn-group roo-upload-cropbox-crop',
31413                 action : 'crop',
31414                 cn : [
31415                     {
31416                         tag : 'button',
31417                         cls : 'btn btn-default',
31418                         html : '<i class="fa fa-crop"></i>'
31419                     }
31420                 ]
31421             },
31422             {
31423                 tag : 'div',
31424                 cls : 'btn-group roo-upload-cropbox-trash',
31425                 action : 'trash',
31426                 cn : [
31427                     {
31428                         tag : 'button',
31429                         cls : 'btn btn-default',
31430                         html : '<i class="fa fa-trash"></i>'
31431                     }
31432                 ]
31433             },
31434             {
31435                 tag : 'div',
31436                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31437                 action : 'rotate-right',
31438                 cn : [
31439                     {
31440                         tag : 'button',
31441                         cls : 'btn btn-default',
31442                         html : '<i class="fa fa-repeat"></i>'
31443                     }
31444                 ]
31445             }
31446         ],
31447         ROTATOR : [
31448             {
31449                 tag : 'div',
31450                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31451                 action : 'rotate-left',
31452                 cn : [
31453                     {
31454                         tag : 'button',
31455                         cls : 'btn btn-default',
31456                         html : '<i class="fa fa-undo"></i>'
31457                     }
31458                 ]
31459             },
31460             {
31461                 tag : 'div',
31462                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31463                 action : 'rotate-right',
31464                 cn : [
31465                     {
31466                         tag : 'button',
31467                         cls : 'btn btn-default',
31468                         html : '<i class="fa fa-repeat"></i>'
31469                     }
31470                 ]
31471             }
31472         ]
31473     }
31474 });
31475
31476 /*
31477 * Licence: LGPL
31478 */
31479
31480 /**
31481  * @class Roo.bootstrap.DocumentManager
31482  * @extends Roo.bootstrap.Component
31483  * Bootstrap DocumentManager class
31484  * @cfg {String} paramName default 'imageUpload'
31485  * @cfg {String} toolTipName default 'filename'
31486  * @cfg {String} method default POST
31487  * @cfg {String} url action url
31488  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31489  * @cfg {Boolean} multiple multiple upload default true
31490  * @cfg {Number} thumbSize default 300
31491  * @cfg {String} fieldLabel
31492  * @cfg {Number} labelWidth default 4
31493  * @cfg {String} labelAlign (left|top) default left
31494  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31495 * @cfg {Number} labellg set the width of label (1-12)
31496  * @cfg {Number} labelmd set the width of label (1-12)
31497  * @cfg {Number} labelsm set the width of label (1-12)
31498  * @cfg {Number} labelxs set the width of label (1-12)
31499  * 
31500  * @constructor
31501  * Create a new DocumentManager
31502  * @param {Object} config The config object
31503  */
31504
31505 Roo.bootstrap.DocumentManager = function(config){
31506     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31507     
31508     this.files = [];
31509     this.delegates = [];
31510     
31511     this.addEvents({
31512         /**
31513          * @event initial
31514          * Fire when initial the DocumentManager
31515          * @param {Roo.bootstrap.DocumentManager} this
31516          */
31517         "initial" : true,
31518         /**
31519          * @event inspect
31520          * inspect selected file
31521          * @param {Roo.bootstrap.DocumentManager} this
31522          * @param {File} file
31523          */
31524         "inspect" : true,
31525         /**
31526          * @event exception
31527          * Fire when xhr load exception
31528          * @param {Roo.bootstrap.DocumentManager} this
31529          * @param {XMLHttpRequest} xhr
31530          */
31531         "exception" : true,
31532         /**
31533          * @event afterupload
31534          * Fire when xhr load exception
31535          * @param {Roo.bootstrap.DocumentManager} this
31536          * @param {XMLHttpRequest} xhr
31537          */
31538         "afterupload" : true,
31539         /**
31540          * @event prepare
31541          * prepare the form data
31542          * @param {Roo.bootstrap.DocumentManager} this
31543          * @param {Object} formData
31544          */
31545         "prepare" : true,
31546         /**
31547          * @event remove
31548          * Fire when remove the file
31549          * @param {Roo.bootstrap.DocumentManager} this
31550          * @param {Object} file
31551          */
31552         "remove" : true,
31553         /**
31554          * @event refresh
31555          * Fire after refresh the file
31556          * @param {Roo.bootstrap.DocumentManager} this
31557          */
31558         "refresh" : true,
31559         /**
31560          * @event click
31561          * Fire after click the image
31562          * @param {Roo.bootstrap.DocumentManager} this
31563          * @param {Object} file
31564          */
31565         "click" : true,
31566         /**
31567          * @event edit
31568          * Fire when upload a image and editable set to true
31569          * @param {Roo.bootstrap.DocumentManager} this
31570          * @param {Object} file
31571          */
31572         "edit" : true,
31573         /**
31574          * @event beforeselectfile
31575          * Fire before select file
31576          * @param {Roo.bootstrap.DocumentManager} this
31577          */
31578         "beforeselectfile" : true,
31579         /**
31580          * @event process
31581          * Fire before process file
31582          * @param {Roo.bootstrap.DocumentManager} this
31583          * @param {Object} file
31584          */
31585         "process" : true,
31586         /**
31587          * @event previewrendered
31588          * Fire when preview rendered
31589          * @param {Roo.bootstrap.DocumentManager} this
31590          * @param {Object} file
31591          */
31592         "previewrendered" : true,
31593         /**
31594          */
31595         "previewResize" : true
31596         
31597     });
31598 };
31599
31600 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31601     
31602     boxes : 0,
31603     inputName : '',
31604     thumbSize : 300,
31605     multiple : true,
31606     files : false,
31607     method : 'POST',
31608     url : '',
31609     paramName : 'imageUpload',
31610     toolTipName : 'filename',
31611     fieldLabel : '',
31612     labelWidth : 4,
31613     labelAlign : 'left',
31614     editable : true,
31615     delegates : false,
31616     xhr : false, 
31617     
31618     labellg : 0,
31619     labelmd : 0,
31620     labelsm : 0,
31621     labelxs : 0,
31622     
31623     getAutoCreate : function()
31624     {   
31625         var managerWidget = {
31626             tag : 'div',
31627             cls : 'roo-document-manager',
31628             cn : [
31629                 {
31630                     tag : 'input',
31631                     cls : 'roo-document-manager-selector',
31632                     type : 'file'
31633                 },
31634                 {
31635                     tag : 'div',
31636                     cls : 'roo-document-manager-uploader',
31637                     cn : [
31638                         {
31639                             tag : 'div',
31640                             cls : 'roo-document-manager-upload-btn',
31641                             html : '<i class="fa fa-plus"></i>'
31642                         }
31643                     ]
31644                     
31645                 }
31646             ]
31647         };
31648         
31649         var content = [
31650             {
31651                 tag : 'div',
31652                 cls : 'column col-md-12',
31653                 cn : managerWidget
31654             }
31655         ];
31656         
31657         if(this.fieldLabel.length){
31658             
31659             content = [
31660                 {
31661                     tag : 'div',
31662                     cls : 'column col-md-12',
31663                     html : this.fieldLabel
31664                 },
31665                 {
31666                     tag : 'div',
31667                     cls : 'column col-md-12',
31668                     cn : managerWidget
31669                 }
31670             ];
31671
31672             if(this.labelAlign == 'left'){
31673                 content = [
31674                     {
31675                         tag : 'div',
31676                         cls : 'column',
31677                         html : this.fieldLabel
31678                     },
31679                     {
31680                         tag : 'div',
31681                         cls : 'column',
31682                         cn : managerWidget
31683                     }
31684                 ];
31685                 
31686                 if(this.labelWidth > 12){
31687                     content[0].style = "width: " + this.labelWidth + 'px';
31688                 }
31689
31690                 if(this.labelWidth < 13 && this.labelmd == 0){
31691                     this.labelmd = this.labelWidth;
31692                 }
31693
31694                 if(this.labellg > 0){
31695                     content[0].cls += ' col-lg-' + this.labellg;
31696                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31697                 }
31698
31699                 if(this.labelmd > 0){
31700                     content[0].cls += ' col-md-' + this.labelmd;
31701                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31702                 }
31703
31704                 if(this.labelsm > 0){
31705                     content[0].cls += ' col-sm-' + this.labelsm;
31706                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31707                 }
31708
31709                 if(this.labelxs > 0){
31710                     content[0].cls += ' col-xs-' + this.labelxs;
31711                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31712                 }
31713                 
31714             }
31715         }
31716         
31717         var cfg = {
31718             tag : 'div',
31719             cls : 'row clearfix',
31720             cn : content
31721         };
31722         
31723         return cfg;
31724         
31725     },
31726     
31727     initEvents : function()
31728     {
31729         this.managerEl = this.el.select('.roo-document-manager', true).first();
31730         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31731         
31732         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31733         this.selectorEl.hide();
31734         
31735         if(this.multiple){
31736             this.selectorEl.attr('multiple', 'multiple');
31737         }
31738         
31739         this.selectorEl.on('change', this.onFileSelected, this);
31740         
31741         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31742         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31743         
31744         this.uploader.on('click', this.onUploaderClick, this);
31745         
31746         this.renderProgressDialog();
31747         
31748         var _this = this;
31749         
31750         window.addEventListener("resize", function() { _this.refresh(); } );
31751         
31752         this.fireEvent('initial', this);
31753     },
31754     
31755     renderProgressDialog : function()
31756     {
31757         var _this = this;
31758         
31759         this.progressDialog = new Roo.bootstrap.Modal({
31760             cls : 'roo-document-manager-progress-dialog',
31761             allow_close : false,
31762             animate : false,
31763             title : '',
31764             buttons : [
31765                 {
31766                     name  :'cancel',
31767                     weight : 'danger',
31768                     html : 'Cancel'
31769                 }
31770             ], 
31771             listeners : { 
31772                 btnclick : function() {
31773                     _this.uploadCancel();
31774                     this.hide();
31775                 }
31776             }
31777         });
31778          
31779         this.progressDialog.render(Roo.get(document.body));
31780          
31781         this.progress = new Roo.bootstrap.Progress({
31782             cls : 'roo-document-manager-progress',
31783             active : true,
31784             striped : true
31785         });
31786         
31787         this.progress.render(this.progressDialog.getChildContainer());
31788         
31789         this.progressBar = new Roo.bootstrap.ProgressBar({
31790             cls : 'roo-document-manager-progress-bar',
31791             aria_valuenow : 0,
31792             aria_valuemin : 0,
31793             aria_valuemax : 12,
31794             panel : 'success'
31795         });
31796         
31797         this.progressBar.render(this.progress.getChildContainer());
31798     },
31799     
31800     onUploaderClick : function(e)
31801     {
31802         e.preventDefault();
31803      
31804         if(this.fireEvent('beforeselectfile', this) != false){
31805             this.selectorEl.dom.click();
31806         }
31807         
31808     },
31809     
31810     onFileSelected : function(e)
31811     {
31812         e.preventDefault();
31813         
31814         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31815             return;
31816         }
31817         
31818         Roo.each(this.selectorEl.dom.files, function(file){
31819             if(this.fireEvent('inspect', this, file) != false){
31820                 this.files.push(file);
31821             }
31822         }, this);
31823         
31824         this.queue();
31825         
31826     },
31827     
31828     queue : function()
31829     {
31830         this.selectorEl.dom.value = '';
31831         
31832         if(!this.files || !this.files.length){
31833             return;
31834         }
31835         
31836         if(this.boxes > 0 && this.files.length > this.boxes){
31837             this.files = this.files.slice(0, this.boxes);
31838         }
31839         
31840         this.uploader.show();
31841         
31842         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31843             this.uploader.hide();
31844         }
31845         
31846         var _this = this;
31847         
31848         var files = [];
31849         
31850         var docs = [];
31851         
31852         Roo.each(this.files, function(file){
31853             
31854             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31855                 var f = this.renderPreview(file);
31856                 files.push(f);
31857                 return;
31858             }
31859             
31860             if(file.type.indexOf('image') != -1){
31861                 this.delegates.push(
31862                     (function(){
31863                         _this.process(file);
31864                     }).createDelegate(this)
31865                 );
31866         
31867                 return;
31868             }
31869             
31870             docs.push(
31871                 (function(){
31872                     _this.process(file);
31873                 }).createDelegate(this)
31874             );
31875             
31876         }, this);
31877         
31878         this.files = files;
31879         
31880         this.delegates = this.delegates.concat(docs);
31881         
31882         if(!this.delegates.length){
31883             this.refresh();
31884             return;
31885         }
31886         
31887         this.progressBar.aria_valuemax = this.delegates.length;
31888         
31889         this.arrange();
31890         
31891         return;
31892     },
31893     
31894     arrange : function()
31895     {
31896         if(!this.delegates.length){
31897             this.progressDialog.hide();
31898             this.refresh();
31899             return;
31900         }
31901         
31902         var delegate = this.delegates.shift();
31903         
31904         this.progressDialog.show();
31905         
31906         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31907         
31908         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31909         
31910         delegate();
31911     },
31912     
31913     refresh : function()
31914     {
31915         this.uploader.show();
31916         
31917         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31918             this.uploader.hide();
31919         }
31920         
31921         Roo.isTouch ? this.closable(false) : this.closable(true);
31922         
31923         this.fireEvent('refresh', this);
31924     },
31925     
31926     onRemove : function(e, el, o)
31927     {
31928         e.preventDefault();
31929         
31930         this.fireEvent('remove', this, o);
31931         
31932     },
31933     
31934     remove : function(o)
31935     {
31936         var files = [];
31937         
31938         Roo.each(this.files, function(file){
31939             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31940                 files.push(file);
31941                 return;
31942             }
31943
31944             o.target.remove();
31945
31946         }, this);
31947         
31948         this.files = files;
31949         
31950         this.refresh();
31951     },
31952     
31953     clear : function()
31954     {
31955         Roo.each(this.files, function(file){
31956             if(!file.target){
31957                 return;
31958             }
31959             
31960             file.target.remove();
31961
31962         }, this);
31963         
31964         this.files = [];
31965         
31966         this.refresh();
31967     },
31968     
31969     onClick : function(e, el, o)
31970     {
31971         e.preventDefault();
31972         
31973         this.fireEvent('click', this, o);
31974         
31975     },
31976     
31977     closable : function(closable)
31978     {
31979         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31980             
31981             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31982             
31983             if(closable){
31984                 el.show();
31985                 return;
31986             }
31987             
31988             el.hide();
31989             
31990         }, this);
31991     },
31992     
31993     xhrOnLoad : function(xhr)
31994     {
31995         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31996             el.remove();
31997         }, this);
31998         
31999         if (xhr.readyState !== 4) {
32000             this.arrange();
32001             this.fireEvent('exception', this, xhr);
32002             return;
32003         }
32004
32005         var response = Roo.decode(xhr.responseText);
32006         
32007         if(!response.success){
32008             this.arrange();
32009             this.fireEvent('exception', this, xhr);
32010             return;
32011         }
32012         
32013         var file = this.renderPreview(response.data);
32014         
32015         this.files.push(file);
32016         
32017         this.arrange();
32018         
32019         this.fireEvent('afterupload', this, xhr);
32020         
32021     },
32022     
32023     xhrOnError : function(xhr)
32024     {
32025         Roo.log('xhr on error');
32026         
32027         var response = Roo.decode(xhr.responseText);
32028           
32029         Roo.log(response);
32030         
32031         this.arrange();
32032     },
32033     
32034     process : function(file)
32035     {
32036         if(this.fireEvent('process', this, file) !== false){
32037             if(this.editable && file.type.indexOf('image') != -1){
32038                 this.fireEvent('edit', this, file);
32039                 return;
32040             }
32041
32042             this.uploadStart(file, false);
32043
32044             return;
32045         }
32046         
32047     },
32048     
32049     uploadStart : function(file, crop)
32050     {
32051         this.xhr = new XMLHttpRequest();
32052         
32053         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32054             this.arrange();
32055             return;
32056         }
32057         
32058         file.xhr = this.xhr;
32059             
32060         this.managerEl.createChild({
32061             tag : 'div',
32062             cls : 'roo-document-manager-loading',
32063             cn : [
32064                 {
32065                     tag : 'div',
32066                     tooltip : file.name,
32067                     cls : 'roo-document-manager-thumb',
32068                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32069                 }
32070             ]
32071
32072         });
32073
32074         this.xhr.open(this.method, this.url, true);
32075         
32076         var headers = {
32077             "Accept": "application/json",
32078             "Cache-Control": "no-cache",
32079             "X-Requested-With": "XMLHttpRequest"
32080         };
32081         
32082         for (var headerName in headers) {
32083             var headerValue = headers[headerName];
32084             if (headerValue) {
32085                 this.xhr.setRequestHeader(headerName, headerValue);
32086             }
32087         }
32088         
32089         var _this = this;
32090         
32091         this.xhr.onload = function()
32092         {
32093             _this.xhrOnLoad(_this.xhr);
32094         }
32095         
32096         this.xhr.onerror = function()
32097         {
32098             _this.xhrOnError(_this.xhr);
32099         }
32100         
32101         var formData = new FormData();
32102
32103         formData.append('returnHTML', 'NO');
32104         
32105         if(crop){
32106             formData.append('crop', crop);
32107         }
32108         
32109         formData.append(this.paramName, file, file.name);
32110         
32111         var options = {
32112             file : file, 
32113             manually : false
32114         };
32115         
32116         if(this.fireEvent('prepare', this, formData, options) != false){
32117             
32118             if(options.manually){
32119                 return;
32120             }
32121             
32122             this.xhr.send(formData);
32123             return;
32124         };
32125         
32126         this.uploadCancel();
32127     },
32128     
32129     uploadCancel : function()
32130     {
32131         if (this.xhr) {
32132             this.xhr.abort();
32133         }
32134         
32135         this.delegates = [];
32136         
32137         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32138             el.remove();
32139         }, this);
32140         
32141         this.arrange();
32142     },
32143     
32144     renderPreview : function(file)
32145     {
32146         if(typeof(file.target) != 'undefined' && file.target){
32147             return file;
32148         }
32149         
32150         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32151         
32152         var previewEl = this.managerEl.createChild({
32153             tag : 'div',
32154             cls : 'roo-document-manager-preview',
32155             cn : [
32156                 {
32157                     tag : 'div',
32158                     tooltip : file[this.toolTipName],
32159                     cls : 'roo-document-manager-thumb',
32160                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32161                 },
32162                 {
32163                     tag : 'button',
32164                     cls : 'close',
32165                     html : '<i class="fa fa-times-circle"></i>'
32166                 }
32167             ]
32168         });
32169
32170         var close = previewEl.select('button.close', true).first();
32171
32172         close.on('click', this.onRemove, this, file);
32173
32174         file.target = previewEl;
32175
32176         var image = previewEl.select('img', true).first();
32177         
32178         var _this = this;
32179         
32180         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32181         
32182         image.on('click', this.onClick, this, file);
32183         
32184         this.fireEvent('previewrendered', this, file);
32185         
32186         return file;
32187         
32188     },
32189     
32190     onPreviewLoad : function(file, image)
32191     {
32192         if(typeof(file.target) == 'undefined' || !file.target){
32193             return;
32194         }
32195         
32196         var width = image.dom.naturalWidth || image.dom.width;
32197         var height = image.dom.naturalHeight || image.dom.height;
32198         
32199         if(!this.previewResize) {
32200             return;
32201         }
32202         
32203         if(width > height){
32204             file.target.addClass('wide');
32205             return;
32206         }
32207         
32208         file.target.addClass('tall');
32209         return;
32210         
32211     },
32212     
32213     uploadFromSource : function(file, crop)
32214     {
32215         this.xhr = new XMLHttpRequest();
32216         
32217         this.managerEl.createChild({
32218             tag : 'div',
32219             cls : 'roo-document-manager-loading',
32220             cn : [
32221                 {
32222                     tag : 'div',
32223                     tooltip : file.name,
32224                     cls : 'roo-document-manager-thumb',
32225                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32226                 }
32227             ]
32228
32229         });
32230
32231         this.xhr.open(this.method, this.url, true);
32232         
32233         var headers = {
32234             "Accept": "application/json",
32235             "Cache-Control": "no-cache",
32236             "X-Requested-With": "XMLHttpRequest"
32237         };
32238         
32239         for (var headerName in headers) {
32240             var headerValue = headers[headerName];
32241             if (headerValue) {
32242                 this.xhr.setRequestHeader(headerName, headerValue);
32243             }
32244         }
32245         
32246         var _this = this;
32247         
32248         this.xhr.onload = function()
32249         {
32250             _this.xhrOnLoad(_this.xhr);
32251         }
32252         
32253         this.xhr.onerror = function()
32254         {
32255             _this.xhrOnError(_this.xhr);
32256         }
32257         
32258         var formData = new FormData();
32259
32260         formData.append('returnHTML', 'NO');
32261         
32262         formData.append('crop', crop);
32263         
32264         if(typeof(file.filename) != 'undefined'){
32265             formData.append('filename', file.filename);
32266         }
32267         
32268         if(typeof(file.mimetype) != 'undefined'){
32269             formData.append('mimetype', file.mimetype);
32270         }
32271         
32272         Roo.log(formData);
32273         
32274         if(this.fireEvent('prepare', this, formData) != false){
32275             this.xhr.send(formData);
32276         };
32277     }
32278 });
32279
32280 /*
32281 * Licence: LGPL
32282 */
32283
32284 /**
32285  * @class Roo.bootstrap.DocumentViewer
32286  * @extends Roo.bootstrap.Component
32287  * Bootstrap DocumentViewer class
32288  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32289  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32290  * 
32291  * @constructor
32292  * Create a new DocumentViewer
32293  * @param {Object} config The config object
32294  */
32295
32296 Roo.bootstrap.DocumentViewer = function(config){
32297     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32298     
32299     this.addEvents({
32300         /**
32301          * @event initial
32302          * Fire after initEvent
32303          * @param {Roo.bootstrap.DocumentViewer} this
32304          */
32305         "initial" : true,
32306         /**
32307          * @event click
32308          * Fire after click
32309          * @param {Roo.bootstrap.DocumentViewer} this
32310          */
32311         "click" : true,
32312         /**
32313          * @event download
32314          * Fire after download button
32315          * @param {Roo.bootstrap.DocumentViewer} this
32316          */
32317         "download" : true,
32318         /**
32319          * @event trash
32320          * Fire after trash button
32321          * @param {Roo.bootstrap.DocumentViewer} this
32322          */
32323         "trash" : true
32324         
32325     });
32326 };
32327
32328 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32329     
32330     showDownload : true,
32331     
32332     showTrash : true,
32333     
32334     getAutoCreate : function()
32335     {
32336         var cfg = {
32337             tag : 'div',
32338             cls : 'roo-document-viewer',
32339             cn : [
32340                 {
32341                     tag : 'div',
32342                     cls : 'roo-document-viewer-body',
32343                     cn : [
32344                         {
32345                             tag : 'div',
32346                             cls : 'roo-document-viewer-thumb',
32347                             cn : [
32348                                 {
32349                                     tag : 'img',
32350                                     cls : 'roo-document-viewer-image'
32351                                 }
32352                             ]
32353                         }
32354                     ]
32355                 },
32356                 {
32357                     tag : 'div',
32358                     cls : 'roo-document-viewer-footer',
32359                     cn : {
32360                         tag : 'div',
32361                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32362                         cn : [
32363                             {
32364                                 tag : 'div',
32365                                 cls : 'btn-group roo-document-viewer-download',
32366                                 cn : [
32367                                     {
32368                                         tag : 'button',
32369                                         cls : 'btn btn-default',
32370                                         html : '<i class="fa fa-download"></i>'
32371                                     }
32372                                 ]
32373                             },
32374                             {
32375                                 tag : 'div',
32376                                 cls : 'btn-group roo-document-viewer-trash',
32377                                 cn : [
32378                                     {
32379                                         tag : 'button',
32380                                         cls : 'btn btn-default',
32381                                         html : '<i class="fa fa-trash"></i>'
32382                                     }
32383                                 ]
32384                             }
32385                         ]
32386                     }
32387                 }
32388             ]
32389         };
32390         
32391         return cfg;
32392     },
32393     
32394     initEvents : function()
32395     {
32396         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32397         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32398         
32399         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32400         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32401         
32402         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32403         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32404         
32405         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32406         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32407         
32408         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32409         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32410         
32411         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32412         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32413         
32414         this.bodyEl.on('click', this.onClick, this);
32415         this.downloadBtn.on('click', this.onDownload, this);
32416         this.trashBtn.on('click', this.onTrash, this);
32417         
32418         this.downloadBtn.hide();
32419         this.trashBtn.hide();
32420         
32421         if(this.showDownload){
32422             this.downloadBtn.show();
32423         }
32424         
32425         if(this.showTrash){
32426             this.trashBtn.show();
32427         }
32428         
32429         if(!this.showDownload && !this.showTrash) {
32430             this.footerEl.hide();
32431         }
32432         
32433     },
32434     
32435     initial : function()
32436     {
32437         this.fireEvent('initial', this);
32438         
32439     },
32440     
32441     onClick : function(e)
32442     {
32443         e.preventDefault();
32444         
32445         this.fireEvent('click', this);
32446     },
32447     
32448     onDownload : function(e)
32449     {
32450         e.preventDefault();
32451         
32452         this.fireEvent('download', this);
32453     },
32454     
32455     onTrash : function(e)
32456     {
32457         e.preventDefault();
32458         
32459         this.fireEvent('trash', this);
32460     }
32461     
32462 });
32463 /*
32464  * - LGPL
32465  *
32466  * nav progress bar
32467  * 
32468  */
32469
32470 /**
32471  * @class Roo.bootstrap.NavProgressBar
32472  * @extends Roo.bootstrap.Component
32473  * Bootstrap NavProgressBar class
32474  * 
32475  * @constructor
32476  * Create a new nav progress bar
32477  * @param {Object} config The config object
32478  */
32479
32480 Roo.bootstrap.NavProgressBar = function(config){
32481     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32482
32483     this.bullets = this.bullets || [];
32484    
32485 //    Roo.bootstrap.NavProgressBar.register(this);
32486      this.addEvents({
32487         /**
32488              * @event changed
32489              * Fires when the active item changes
32490              * @param {Roo.bootstrap.NavProgressBar} this
32491              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32492              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32493          */
32494         'changed': true
32495      });
32496     
32497 };
32498
32499 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32500     
32501     bullets : [],
32502     barItems : [],
32503     
32504     getAutoCreate : function()
32505     {
32506         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32507         
32508         cfg = {
32509             tag : 'div',
32510             cls : 'roo-navigation-bar-group',
32511             cn : [
32512                 {
32513                     tag : 'div',
32514                     cls : 'roo-navigation-top-bar'
32515                 },
32516                 {
32517                     tag : 'div',
32518                     cls : 'roo-navigation-bullets-bar',
32519                     cn : [
32520                         {
32521                             tag : 'ul',
32522                             cls : 'roo-navigation-bar'
32523                         }
32524                     ]
32525                 },
32526                 
32527                 {
32528                     tag : 'div',
32529                     cls : 'roo-navigation-bottom-bar'
32530                 }
32531             ]
32532             
32533         };
32534         
32535         return cfg;
32536         
32537     },
32538     
32539     initEvents: function() 
32540     {
32541         
32542     },
32543     
32544     onRender : function(ct, position) 
32545     {
32546         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32547         
32548         if(this.bullets.length){
32549             Roo.each(this.bullets, function(b){
32550                this.addItem(b);
32551             }, this);
32552         }
32553         
32554         this.format();
32555         
32556     },
32557     
32558     addItem : function(cfg)
32559     {
32560         var item = new Roo.bootstrap.NavProgressItem(cfg);
32561         
32562         item.parentId = this.id;
32563         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32564         
32565         if(cfg.html){
32566             var top = new Roo.bootstrap.Element({
32567                 tag : 'div',
32568                 cls : 'roo-navigation-bar-text'
32569             });
32570             
32571             var bottom = new Roo.bootstrap.Element({
32572                 tag : 'div',
32573                 cls : 'roo-navigation-bar-text'
32574             });
32575             
32576             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32577             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32578             
32579             var topText = new Roo.bootstrap.Element({
32580                 tag : 'span',
32581                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32582             });
32583             
32584             var bottomText = new Roo.bootstrap.Element({
32585                 tag : 'span',
32586                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32587             });
32588             
32589             topText.onRender(top.el, null);
32590             bottomText.onRender(bottom.el, null);
32591             
32592             item.topEl = top;
32593             item.bottomEl = bottom;
32594         }
32595         
32596         this.barItems.push(item);
32597         
32598         return item;
32599     },
32600     
32601     getActive : function()
32602     {
32603         var active = false;
32604         
32605         Roo.each(this.barItems, function(v){
32606             
32607             if (!v.isActive()) {
32608                 return;
32609             }
32610             
32611             active = v;
32612             return false;
32613             
32614         });
32615         
32616         return active;
32617     },
32618     
32619     setActiveItem : function(item)
32620     {
32621         var prev = false;
32622         
32623         Roo.each(this.barItems, function(v){
32624             if (v.rid == item.rid) {
32625                 return ;
32626             }
32627             
32628             if (v.isActive()) {
32629                 v.setActive(false);
32630                 prev = v;
32631             }
32632         });
32633
32634         item.setActive(true);
32635         
32636         this.fireEvent('changed', this, item, prev);
32637     },
32638     
32639     getBarItem: function(rid)
32640     {
32641         var ret = false;
32642         
32643         Roo.each(this.barItems, function(e) {
32644             if (e.rid != rid) {
32645                 return;
32646             }
32647             
32648             ret =  e;
32649             return false;
32650         });
32651         
32652         return ret;
32653     },
32654     
32655     indexOfItem : function(item)
32656     {
32657         var index = false;
32658         
32659         Roo.each(this.barItems, function(v, i){
32660             
32661             if (v.rid != item.rid) {
32662                 return;
32663             }
32664             
32665             index = i;
32666             return false
32667         });
32668         
32669         return index;
32670     },
32671     
32672     setActiveNext : function()
32673     {
32674         var i = this.indexOfItem(this.getActive());
32675         
32676         if (i > this.barItems.length) {
32677             return;
32678         }
32679         
32680         this.setActiveItem(this.barItems[i+1]);
32681     },
32682     
32683     setActivePrev : function()
32684     {
32685         var i = this.indexOfItem(this.getActive());
32686         
32687         if (i  < 1) {
32688             return;
32689         }
32690         
32691         this.setActiveItem(this.barItems[i-1]);
32692     },
32693     
32694     format : function()
32695     {
32696         if(!this.barItems.length){
32697             return;
32698         }
32699      
32700         var width = 100 / this.barItems.length;
32701         
32702         Roo.each(this.barItems, function(i){
32703             i.el.setStyle('width', width + '%');
32704             i.topEl.el.setStyle('width', width + '%');
32705             i.bottomEl.el.setStyle('width', width + '%');
32706         }, this);
32707         
32708     }
32709     
32710 });
32711 /*
32712  * - LGPL
32713  *
32714  * Nav Progress Item
32715  * 
32716  */
32717
32718 /**
32719  * @class Roo.bootstrap.NavProgressItem
32720  * @extends Roo.bootstrap.Component
32721  * Bootstrap NavProgressItem class
32722  * @cfg {String} rid the reference id
32723  * @cfg {Boolean} active (true|false) Is item active default false
32724  * @cfg {Boolean} disabled (true|false) Is item active default false
32725  * @cfg {String} html
32726  * @cfg {String} position (top|bottom) text position default bottom
32727  * @cfg {String} icon show icon instead of number
32728  * 
32729  * @constructor
32730  * Create a new NavProgressItem
32731  * @param {Object} config The config object
32732  */
32733 Roo.bootstrap.NavProgressItem = function(config){
32734     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32735     this.addEvents({
32736         // raw events
32737         /**
32738          * @event click
32739          * The raw click event for the entire grid.
32740          * @param {Roo.bootstrap.NavProgressItem} this
32741          * @param {Roo.EventObject} e
32742          */
32743         "click" : true
32744     });
32745    
32746 };
32747
32748 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32749     
32750     rid : '',
32751     active : false,
32752     disabled : false,
32753     html : '',
32754     position : 'bottom',
32755     icon : false,
32756     
32757     getAutoCreate : function()
32758     {
32759         var iconCls = 'roo-navigation-bar-item-icon';
32760         
32761         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32762         
32763         var cfg = {
32764             tag: 'li',
32765             cls: 'roo-navigation-bar-item',
32766             cn : [
32767                 {
32768                     tag : 'i',
32769                     cls : iconCls
32770                 }
32771             ]
32772         };
32773         
32774         if(this.active){
32775             cfg.cls += ' active';
32776         }
32777         if(this.disabled){
32778             cfg.cls += ' disabled';
32779         }
32780         
32781         return cfg;
32782     },
32783     
32784     disable : function()
32785     {
32786         this.setDisabled(true);
32787     },
32788     
32789     enable : function()
32790     {
32791         this.setDisabled(false);
32792     },
32793     
32794     initEvents: function() 
32795     {
32796         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32797         
32798         this.iconEl.on('click', this.onClick, this);
32799     },
32800     
32801     onClick : function(e)
32802     {
32803         e.preventDefault();
32804         
32805         if(this.disabled){
32806             return;
32807         }
32808         
32809         if(this.fireEvent('click', this, e) === false){
32810             return;
32811         };
32812         
32813         this.parent().setActiveItem(this);
32814     },
32815     
32816     isActive: function () 
32817     {
32818         return this.active;
32819     },
32820     
32821     setActive : function(state)
32822     {
32823         if(this.active == state){
32824             return;
32825         }
32826         
32827         this.active = state;
32828         
32829         if (state) {
32830             this.el.addClass('active');
32831             return;
32832         }
32833         
32834         this.el.removeClass('active');
32835         
32836         return;
32837     },
32838     
32839     setDisabled : function(state)
32840     {
32841         if(this.disabled == state){
32842             return;
32843         }
32844         
32845         this.disabled = state;
32846         
32847         if (state) {
32848             this.el.addClass('disabled');
32849             return;
32850         }
32851         
32852         this.el.removeClass('disabled');
32853     },
32854     
32855     tooltipEl : function()
32856     {
32857         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32858     }
32859 });
32860  
32861
32862  /*
32863  * - LGPL
32864  *
32865  * FieldLabel
32866  * 
32867  */
32868
32869 /**
32870  * @class Roo.bootstrap.FieldLabel
32871  * @extends Roo.bootstrap.Component
32872  * Bootstrap FieldLabel class
32873  * @cfg {String} html contents of the element
32874  * @cfg {String} tag tag of the element default label
32875  * @cfg {String} cls class of the element
32876  * @cfg {String} target label target 
32877  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32878  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32879  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32880  * @cfg {String} iconTooltip default "This field is required"
32881  * @cfg {String} indicatorpos (left|right) default left
32882  * 
32883  * @constructor
32884  * Create a new FieldLabel
32885  * @param {Object} config The config object
32886  */
32887
32888 Roo.bootstrap.FieldLabel = function(config){
32889     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32890     
32891     this.addEvents({
32892             /**
32893              * @event invalid
32894              * Fires after the field has been marked as invalid.
32895              * @param {Roo.form.FieldLabel} this
32896              * @param {String} msg The validation message
32897              */
32898             invalid : true,
32899             /**
32900              * @event valid
32901              * Fires after the field has been validated with no errors.
32902              * @param {Roo.form.FieldLabel} this
32903              */
32904             valid : true
32905         });
32906 };
32907
32908 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32909     
32910     tag: 'label',
32911     cls: '',
32912     html: '',
32913     target: '',
32914     allowBlank : true,
32915     invalidClass : 'has-warning',
32916     validClass : 'has-success',
32917     iconTooltip : 'This field is required',
32918     indicatorpos : 'left',
32919     
32920     getAutoCreate : function(){
32921         
32922         var cls = "";
32923         if (!this.allowBlank) {
32924             cls  = "visible";
32925         }
32926         
32927         var cfg = {
32928             tag : this.tag,
32929             cls : 'roo-bootstrap-field-label ' + this.cls,
32930             for : this.target,
32931             cn : [
32932                 {
32933                     tag : 'i',
32934                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32935                     tooltip : this.iconTooltip
32936                 },
32937                 {
32938                     tag : 'span',
32939                     html : this.html
32940                 }
32941             ] 
32942         };
32943         
32944         if(this.indicatorpos == 'right'){
32945             var cfg = {
32946                 tag : this.tag,
32947                 cls : 'roo-bootstrap-field-label ' + this.cls,
32948                 for : this.target,
32949                 cn : [
32950                     {
32951                         tag : 'span',
32952                         html : this.html
32953                     },
32954                     {
32955                         tag : 'i',
32956                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32957                         tooltip : this.iconTooltip
32958                     }
32959                 ] 
32960             };
32961         }
32962         
32963         return cfg;
32964     },
32965     
32966     initEvents: function() 
32967     {
32968         Roo.bootstrap.Element.superclass.initEvents.call(this);
32969         
32970         this.indicator = this.indicatorEl();
32971         
32972         if(this.indicator){
32973             this.indicator.removeClass('visible');
32974             this.indicator.addClass('invisible');
32975         }
32976         
32977         Roo.bootstrap.FieldLabel.register(this);
32978     },
32979     
32980     indicatorEl : function()
32981     {
32982         var indicator = this.el.select('i.roo-required-indicator',true).first();
32983         
32984         if(!indicator){
32985             return false;
32986         }
32987         
32988         return indicator;
32989         
32990     },
32991     
32992     /**
32993      * Mark this field as valid
32994      */
32995     markValid : function()
32996     {
32997         if(this.indicator){
32998             this.indicator.removeClass('visible');
32999             this.indicator.addClass('invisible');
33000         }
33001         if (Roo.bootstrap.version == 3) {
33002             this.el.removeClass(this.invalidClass);
33003             this.el.addClass(this.validClass);
33004         } else {
33005             this.el.removeClass('is-invalid');
33006             this.el.addClass('is-valid');
33007         }
33008         
33009         
33010         this.fireEvent('valid', this);
33011     },
33012     
33013     /**
33014      * Mark this field as invalid
33015      * @param {String} msg The validation message
33016      */
33017     markInvalid : function(msg)
33018     {
33019         if(this.indicator){
33020             this.indicator.removeClass('invisible');
33021             this.indicator.addClass('visible');
33022         }
33023           if (Roo.bootstrap.version == 3) {
33024             this.el.removeClass(this.validClass);
33025             this.el.addClass(this.invalidClass);
33026         } else {
33027             this.el.removeClass('is-valid');
33028             this.el.addClass('is-invalid');
33029         }
33030         
33031         
33032         this.fireEvent('invalid', this, msg);
33033     }
33034     
33035    
33036 });
33037
33038 Roo.apply(Roo.bootstrap.FieldLabel, {
33039     
33040     groups: {},
33041     
33042      /**
33043     * register a FieldLabel Group
33044     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33045     */
33046     register : function(label)
33047     {
33048         if(this.groups.hasOwnProperty(label.target)){
33049             return;
33050         }
33051      
33052         this.groups[label.target] = label;
33053         
33054     },
33055     /**
33056     * fetch a FieldLabel Group based on the target
33057     * @param {string} target
33058     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33059     */
33060     get: function(target) {
33061         if (typeof(this.groups[target]) == 'undefined') {
33062             return false;
33063         }
33064         
33065         return this.groups[target] ;
33066     }
33067 });
33068
33069  
33070
33071  /*
33072  * - LGPL
33073  *
33074  * page DateSplitField.
33075  * 
33076  */
33077
33078
33079 /**
33080  * @class Roo.bootstrap.DateSplitField
33081  * @extends Roo.bootstrap.Component
33082  * Bootstrap DateSplitField class
33083  * @cfg {string} fieldLabel - the label associated
33084  * @cfg {Number} labelWidth set the width of label (0-12)
33085  * @cfg {String} labelAlign (top|left)
33086  * @cfg {Boolean} dayAllowBlank (true|false) default false
33087  * @cfg {Boolean} monthAllowBlank (true|false) default false
33088  * @cfg {Boolean} yearAllowBlank (true|false) default false
33089  * @cfg {string} dayPlaceholder 
33090  * @cfg {string} monthPlaceholder
33091  * @cfg {string} yearPlaceholder
33092  * @cfg {string} dayFormat default 'd'
33093  * @cfg {string} monthFormat default 'm'
33094  * @cfg {string} yearFormat default 'Y'
33095  * @cfg {Number} labellg set the width of label (1-12)
33096  * @cfg {Number} labelmd set the width of label (1-12)
33097  * @cfg {Number} labelsm set the width of label (1-12)
33098  * @cfg {Number} labelxs set the width of label (1-12)
33099
33100  *     
33101  * @constructor
33102  * Create a new DateSplitField
33103  * @param {Object} config The config object
33104  */
33105
33106 Roo.bootstrap.DateSplitField = function(config){
33107     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33108     
33109     this.addEvents({
33110         // raw events
33111          /**
33112          * @event years
33113          * getting the data of years
33114          * @param {Roo.bootstrap.DateSplitField} this
33115          * @param {Object} years
33116          */
33117         "years" : true,
33118         /**
33119          * @event days
33120          * getting the data of days
33121          * @param {Roo.bootstrap.DateSplitField} this
33122          * @param {Object} days
33123          */
33124         "days" : true,
33125         /**
33126          * @event invalid
33127          * Fires after the field has been marked as invalid.
33128          * @param {Roo.form.Field} this
33129          * @param {String} msg The validation message
33130          */
33131         invalid : true,
33132        /**
33133          * @event valid
33134          * Fires after the field has been validated with no errors.
33135          * @param {Roo.form.Field} this
33136          */
33137         valid : true
33138     });
33139 };
33140
33141 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33142     
33143     fieldLabel : '',
33144     labelAlign : 'top',
33145     labelWidth : 3,
33146     dayAllowBlank : false,
33147     monthAllowBlank : false,
33148     yearAllowBlank : false,
33149     dayPlaceholder : '',
33150     monthPlaceholder : '',
33151     yearPlaceholder : '',
33152     dayFormat : 'd',
33153     monthFormat : 'm',
33154     yearFormat : 'Y',
33155     isFormField : true,
33156     labellg : 0,
33157     labelmd : 0,
33158     labelsm : 0,
33159     labelxs : 0,
33160     
33161     getAutoCreate : function()
33162     {
33163         var cfg = {
33164             tag : 'div',
33165             cls : 'row roo-date-split-field-group',
33166             cn : [
33167                 {
33168                     tag : 'input',
33169                     type : 'hidden',
33170                     cls : 'form-hidden-field roo-date-split-field-group-value',
33171                     name : this.name
33172                 }
33173             ]
33174         };
33175         
33176         var labelCls = 'col-md-12';
33177         var contentCls = 'col-md-4';
33178         
33179         if(this.fieldLabel){
33180             
33181             var label = {
33182                 tag : 'div',
33183                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33184                 cn : [
33185                     {
33186                         tag : 'label',
33187                         html : this.fieldLabel
33188                     }
33189                 ]
33190             };
33191             
33192             if(this.labelAlign == 'left'){
33193             
33194                 if(this.labelWidth > 12){
33195                     label.style = "width: " + this.labelWidth + 'px';
33196                 }
33197
33198                 if(this.labelWidth < 13 && this.labelmd == 0){
33199                     this.labelmd = this.labelWidth;
33200                 }
33201
33202                 if(this.labellg > 0){
33203                     labelCls = ' col-lg-' + this.labellg;
33204                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33205                 }
33206
33207                 if(this.labelmd > 0){
33208                     labelCls = ' col-md-' + this.labelmd;
33209                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33210                 }
33211
33212                 if(this.labelsm > 0){
33213                     labelCls = ' col-sm-' + this.labelsm;
33214                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33215                 }
33216
33217                 if(this.labelxs > 0){
33218                     labelCls = ' col-xs-' + this.labelxs;
33219                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33220                 }
33221             }
33222             
33223             label.cls += ' ' + labelCls;
33224             
33225             cfg.cn.push(label);
33226         }
33227         
33228         Roo.each(['day', 'month', 'year'], function(t){
33229             cfg.cn.push({
33230                 tag : 'div',
33231                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33232             });
33233         }, this);
33234         
33235         return cfg;
33236     },
33237     
33238     inputEl: function ()
33239     {
33240         return this.el.select('.roo-date-split-field-group-value', true).first();
33241     },
33242     
33243     onRender : function(ct, position) 
33244     {
33245         var _this = this;
33246         
33247         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33248         
33249         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33250         
33251         this.dayField = new Roo.bootstrap.ComboBox({
33252             allowBlank : this.dayAllowBlank,
33253             alwaysQuery : true,
33254             displayField : 'value',
33255             editable : false,
33256             fieldLabel : '',
33257             forceSelection : true,
33258             mode : 'local',
33259             placeholder : this.dayPlaceholder,
33260             selectOnFocus : true,
33261             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33262             triggerAction : 'all',
33263             typeAhead : true,
33264             valueField : 'value',
33265             store : new Roo.data.SimpleStore({
33266                 data : (function() {    
33267                     var days = [];
33268                     _this.fireEvent('days', _this, days);
33269                     return days;
33270                 })(),
33271                 fields : [ 'value' ]
33272             }),
33273             listeners : {
33274                 select : function (_self, record, index)
33275                 {
33276                     _this.setValue(_this.getValue());
33277                 }
33278             }
33279         });
33280
33281         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33282         
33283         this.monthField = new Roo.bootstrap.MonthField({
33284             after : '<i class=\"fa fa-calendar\"></i>',
33285             allowBlank : this.monthAllowBlank,
33286             placeholder : this.monthPlaceholder,
33287             readOnly : true,
33288             listeners : {
33289                 render : function (_self)
33290                 {
33291                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33292                         e.preventDefault();
33293                         _self.focus();
33294                     });
33295                 },
33296                 select : function (_self, oldvalue, newvalue)
33297                 {
33298                     _this.setValue(_this.getValue());
33299                 }
33300             }
33301         });
33302         
33303         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33304         
33305         this.yearField = new Roo.bootstrap.ComboBox({
33306             allowBlank : this.yearAllowBlank,
33307             alwaysQuery : true,
33308             displayField : 'value',
33309             editable : false,
33310             fieldLabel : '',
33311             forceSelection : true,
33312             mode : 'local',
33313             placeholder : this.yearPlaceholder,
33314             selectOnFocus : true,
33315             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33316             triggerAction : 'all',
33317             typeAhead : true,
33318             valueField : 'value',
33319             store : new Roo.data.SimpleStore({
33320                 data : (function() {
33321                     var years = [];
33322                     _this.fireEvent('years', _this, years);
33323                     return years;
33324                 })(),
33325                 fields : [ 'value' ]
33326             }),
33327             listeners : {
33328                 select : function (_self, record, index)
33329                 {
33330                     _this.setValue(_this.getValue());
33331                 }
33332             }
33333         });
33334
33335         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33336     },
33337     
33338     setValue : function(v, format)
33339     {
33340         this.inputEl.dom.value = v;
33341         
33342         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33343         
33344         var d = Date.parseDate(v, f);
33345         
33346         if(!d){
33347             this.validate();
33348             return;
33349         }
33350         
33351         this.setDay(d.format(this.dayFormat));
33352         this.setMonth(d.format(this.monthFormat));
33353         this.setYear(d.format(this.yearFormat));
33354         
33355         this.validate();
33356         
33357         return;
33358     },
33359     
33360     setDay : function(v)
33361     {
33362         this.dayField.setValue(v);
33363         this.inputEl.dom.value = this.getValue();
33364         this.validate();
33365         return;
33366     },
33367     
33368     setMonth : function(v)
33369     {
33370         this.monthField.setValue(v, true);
33371         this.inputEl.dom.value = this.getValue();
33372         this.validate();
33373         return;
33374     },
33375     
33376     setYear : function(v)
33377     {
33378         this.yearField.setValue(v);
33379         this.inputEl.dom.value = this.getValue();
33380         this.validate();
33381         return;
33382     },
33383     
33384     getDay : function()
33385     {
33386         return this.dayField.getValue();
33387     },
33388     
33389     getMonth : function()
33390     {
33391         return this.monthField.getValue();
33392     },
33393     
33394     getYear : function()
33395     {
33396         return this.yearField.getValue();
33397     },
33398     
33399     getValue : function()
33400     {
33401         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33402         
33403         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33404         
33405         return date;
33406     },
33407     
33408     reset : function()
33409     {
33410         this.setDay('');
33411         this.setMonth('');
33412         this.setYear('');
33413         this.inputEl.dom.value = '';
33414         this.validate();
33415         return;
33416     },
33417     
33418     validate : function()
33419     {
33420         var d = this.dayField.validate();
33421         var m = this.monthField.validate();
33422         var y = this.yearField.validate();
33423         
33424         var valid = true;
33425         
33426         if(
33427                 (!this.dayAllowBlank && !d) ||
33428                 (!this.monthAllowBlank && !m) ||
33429                 (!this.yearAllowBlank && !y)
33430         ){
33431             valid = false;
33432         }
33433         
33434         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33435             return valid;
33436         }
33437         
33438         if(valid){
33439             this.markValid();
33440             return valid;
33441         }
33442         
33443         this.markInvalid();
33444         
33445         return valid;
33446     },
33447     
33448     markValid : function()
33449     {
33450         
33451         var label = this.el.select('label', true).first();
33452         var icon = this.el.select('i.fa-star', true).first();
33453
33454         if(label && icon){
33455             icon.remove();
33456         }
33457         
33458         this.fireEvent('valid', this);
33459     },
33460     
33461      /**
33462      * Mark this field as invalid
33463      * @param {String} msg The validation message
33464      */
33465     markInvalid : function(msg)
33466     {
33467         
33468         var label = this.el.select('label', true).first();
33469         var icon = this.el.select('i.fa-star', true).first();
33470
33471         if(label && !icon){
33472             this.el.select('.roo-date-split-field-label', true).createChild({
33473                 tag : 'i',
33474                 cls : 'text-danger fa fa-lg fa-star',
33475                 tooltip : 'This field is required',
33476                 style : 'margin-right:5px;'
33477             }, label, true);
33478         }
33479         
33480         this.fireEvent('invalid', this, msg);
33481     },
33482     
33483     clearInvalid : function()
33484     {
33485         var label = this.el.select('label', true).first();
33486         var icon = this.el.select('i.fa-star', true).first();
33487
33488         if(label && icon){
33489             icon.remove();
33490         }
33491         
33492         this.fireEvent('valid', this);
33493     },
33494     
33495     getName: function()
33496     {
33497         return this.name;
33498     }
33499     
33500 });
33501
33502  /**
33503  *
33504  * This is based on 
33505  * http://masonry.desandro.com
33506  *
33507  * The idea is to render all the bricks based on vertical width...
33508  *
33509  * The original code extends 'outlayer' - we might need to use that....
33510  * 
33511  */
33512
33513
33514 /**
33515  * @class Roo.bootstrap.LayoutMasonry
33516  * @extends Roo.bootstrap.Component
33517  * Bootstrap Layout Masonry class
33518  * 
33519  * @constructor
33520  * Create a new Element
33521  * @param {Object} config The config object
33522  */
33523
33524 Roo.bootstrap.LayoutMasonry = function(config){
33525     
33526     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33527     
33528     this.bricks = [];
33529     
33530     Roo.bootstrap.LayoutMasonry.register(this);
33531     
33532     this.addEvents({
33533         // raw events
33534         /**
33535          * @event layout
33536          * Fire after layout the items
33537          * @param {Roo.bootstrap.LayoutMasonry} this
33538          * @param {Roo.EventObject} e
33539          */
33540         "layout" : true
33541     });
33542     
33543 };
33544
33545 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33546     
33547     /**
33548      * @cfg {Boolean} isLayoutInstant = no animation?
33549      */   
33550     isLayoutInstant : false, // needed?
33551    
33552     /**
33553      * @cfg {Number} boxWidth  width of the columns
33554      */   
33555     boxWidth : 450,
33556     
33557       /**
33558      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33559      */   
33560     boxHeight : 0,
33561     
33562     /**
33563      * @cfg {Number} padWidth padding below box..
33564      */   
33565     padWidth : 10, 
33566     
33567     /**
33568      * @cfg {Number} gutter gutter width..
33569      */   
33570     gutter : 10,
33571     
33572      /**
33573      * @cfg {Number} maxCols maximum number of columns
33574      */   
33575     
33576     maxCols: 0,
33577     
33578     /**
33579      * @cfg {Boolean} isAutoInitial defalut true
33580      */   
33581     isAutoInitial : true, 
33582     
33583     containerWidth: 0,
33584     
33585     /**
33586      * @cfg {Boolean} isHorizontal defalut false
33587      */   
33588     isHorizontal : false, 
33589
33590     currentSize : null,
33591     
33592     tag: 'div',
33593     
33594     cls: '',
33595     
33596     bricks: null, //CompositeElement
33597     
33598     cols : 1,
33599     
33600     _isLayoutInited : false,
33601     
33602 //    isAlternative : false, // only use for vertical layout...
33603     
33604     /**
33605      * @cfg {Number} alternativePadWidth padding below box..
33606      */   
33607     alternativePadWidth : 50,
33608     
33609     selectedBrick : [],
33610     
33611     getAutoCreate : function(){
33612         
33613         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33614         
33615         var cfg = {
33616             tag: this.tag,
33617             cls: 'blog-masonary-wrapper ' + this.cls,
33618             cn : {
33619                 cls : 'mas-boxes masonary'
33620             }
33621         };
33622         
33623         return cfg;
33624     },
33625     
33626     getChildContainer: function( )
33627     {
33628         if (this.boxesEl) {
33629             return this.boxesEl;
33630         }
33631         
33632         this.boxesEl = this.el.select('.mas-boxes').first();
33633         
33634         return this.boxesEl;
33635     },
33636     
33637     
33638     initEvents : function()
33639     {
33640         var _this = this;
33641         
33642         if(this.isAutoInitial){
33643             Roo.log('hook children rendered');
33644             this.on('childrenrendered', function() {
33645                 Roo.log('children rendered');
33646                 _this.initial();
33647             } ,this);
33648         }
33649     },
33650     
33651     initial : function()
33652     {
33653         this.selectedBrick = [];
33654         
33655         this.currentSize = this.el.getBox(true);
33656         
33657         Roo.EventManager.onWindowResize(this.resize, this); 
33658
33659         if(!this.isAutoInitial){
33660             this.layout();
33661             return;
33662         }
33663         
33664         this.layout();
33665         
33666         return;
33667         //this.layout.defer(500,this);
33668         
33669     },
33670     
33671     resize : function()
33672     {
33673         var cs = this.el.getBox(true);
33674         
33675         if (
33676                 this.currentSize.width == cs.width && 
33677                 this.currentSize.x == cs.x && 
33678                 this.currentSize.height == cs.height && 
33679                 this.currentSize.y == cs.y 
33680         ) {
33681             Roo.log("no change in with or X or Y");
33682             return;
33683         }
33684         
33685         this.currentSize = cs;
33686         
33687         this.layout();
33688         
33689     },
33690     
33691     layout : function()
33692     {   
33693         this._resetLayout();
33694         
33695         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33696         
33697         this.layoutItems( isInstant );
33698       
33699         this._isLayoutInited = true;
33700         
33701         this.fireEvent('layout', this);
33702         
33703     },
33704     
33705     _resetLayout : function()
33706     {
33707         if(this.isHorizontal){
33708             this.horizontalMeasureColumns();
33709             return;
33710         }
33711         
33712         this.verticalMeasureColumns();
33713         
33714     },
33715     
33716     verticalMeasureColumns : function()
33717     {
33718         this.getContainerWidth();
33719         
33720 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33721 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33722 //            return;
33723 //        }
33724         
33725         var boxWidth = this.boxWidth + this.padWidth;
33726         
33727         if(this.containerWidth < this.boxWidth){
33728             boxWidth = this.containerWidth
33729         }
33730         
33731         var containerWidth = this.containerWidth;
33732         
33733         var cols = Math.floor(containerWidth / boxWidth);
33734         
33735         this.cols = Math.max( cols, 1 );
33736         
33737         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33738         
33739         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33740         
33741         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33742         
33743         this.colWidth = boxWidth + avail - this.padWidth;
33744         
33745         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33746         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33747     },
33748     
33749     horizontalMeasureColumns : function()
33750     {
33751         this.getContainerWidth();
33752         
33753         var boxWidth = this.boxWidth;
33754         
33755         if(this.containerWidth < boxWidth){
33756             boxWidth = this.containerWidth;
33757         }
33758         
33759         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33760         
33761         this.el.setHeight(boxWidth);
33762         
33763     },
33764     
33765     getContainerWidth : function()
33766     {
33767         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33768     },
33769     
33770     layoutItems : function( isInstant )
33771     {
33772         Roo.log(this.bricks);
33773         
33774         var items = Roo.apply([], this.bricks);
33775         
33776         if(this.isHorizontal){
33777             this._horizontalLayoutItems( items , isInstant );
33778             return;
33779         }
33780         
33781 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33782 //            this._verticalAlternativeLayoutItems( items , isInstant );
33783 //            return;
33784 //        }
33785         
33786         this._verticalLayoutItems( items , isInstant );
33787         
33788     },
33789     
33790     _verticalLayoutItems : function ( items , isInstant)
33791     {
33792         if ( !items || !items.length ) {
33793             return;
33794         }
33795         
33796         var standard = [
33797             ['xs', 'xs', 'xs', 'tall'],
33798             ['xs', 'xs', 'tall'],
33799             ['xs', 'xs', 'sm'],
33800             ['xs', 'xs', 'xs'],
33801             ['xs', 'tall'],
33802             ['xs', 'sm'],
33803             ['xs', 'xs'],
33804             ['xs'],
33805             
33806             ['sm', 'xs', 'xs'],
33807             ['sm', 'xs'],
33808             ['sm'],
33809             
33810             ['tall', 'xs', 'xs', 'xs'],
33811             ['tall', 'xs', 'xs'],
33812             ['tall', 'xs'],
33813             ['tall']
33814             
33815         ];
33816         
33817         var queue = [];
33818         
33819         var boxes = [];
33820         
33821         var box = [];
33822         
33823         Roo.each(items, function(item, k){
33824             
33825             switch (item.size) {
33826                 // these layouts take up a full box,
33827                 case 'md' :
33828                 case 'md-left' :
33829                 case 'md-right' :
33830                 case 'wide' :
33831                     
33832                     if(box.length){
33833                         boxes.push(box);
33834                         box = [];
33835                     }
33836                     
33837                     boxes.push([item]);
33838                     
33839                     break;
33840                     
33841                 case 'xs' :
33842                 case 'sm' :
33843                 case 'tall' :
33844                     
33845                     box.push(item);
33846                     
33847                     break;
33848                 default :
33849                     break;
33850                     
33851             }
33852             
33853         }, this);
33854         
33855         if(box.length){
33856             boxes.push(box);
33857             box = [];
33858         }
33859         
33860         var filterPattern = function(box, length)
33861         {
33862             if(!box.length){
33863                 return;
33864             }
33865             
33866             var match = false;
33867             
33868             var pattern = box.slice(0, length);
33869             
33870             var format = [];
33871             
33872             Roo.each(pattern, function(i){
33873                 format.push(i.size);
33874             }, this);
33875             
33876             Roo.each(standard, function(s){
33877                 
33878                 if(String(s) != String(format)){
33879                     return;
33880                 }
33881                 
33882                 match = true;
33883                 return false;
33884                 
33885             }, this);
33886             
33887             if(!match && length == 1){
33888                 return;
33889             }
33890             
33891             if(!match){
33892                 filterPattern(box, length - 1);
33893                 return;
33894             }
33895                 
33896             queue.push(pattern);
33897
33898             box = box.slice(length, box.length);
33899
33900             filterPattern(box, 4);
33901
33902             return;
33903             
33904         }
33905         
33906         Roo.each(boxes, function(box, k){
33907             
33908             if(!box.length){
33909                 return;
33910             }
33911             
33912             if(box.length == 1){
33913                 queue.push(box);
33914                 return;
33915             }
33916             
33917             filterPattern(box, 4);
33918             
33919         }, this);
33920         
33921         this._processVerticalLayoutQueue( queue, isInstant );
33922         
33923     },
33924     
33925 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33926 //    {
33927 //        if ( !items || !items.length ) {
33928 //            return;
33929 //        }
33930 //
33931 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33932 //        
33933 //    },
33934     
33935     _horizontalLayoutItems : function ( items , isInstant)
33936     {
33937         if ( !items || !items.length || items.length < 3) {
33938             return;
33939         }
33940         
33941         items.reverse();
33942         
33943         var eItems = items.slice(0, 3);
33944         
33945         items = items.slice(3, items.length);
33946         
33947         var standard = [
33948             ['xs', 'xs', 'xs', 'wide'],
33949             ['xs', 'xs', 'wide'],
33950             ['xs', 'xs', 'sm'],
33951             ['xs', 'xs', 'xs'],
33952             ['xs', 'wide'],
33953             ['xs', 'sm'],
33954             ['xs', 'xs'],
33955             ['xs'],
33956             
33957             ['sm', 'xs', 'xs'],
33958             ['sm', 'xs'],
33959             ['sm'],
33960             
33961             ['wide', 'xs', 'xs', 'xs'],
33962             ['wide', 'xs', 'xs'],
33963             ['wide', 'xs'],
33964             ['wide'],
33965             
33966             ['wide-thin']
33967         ];
33968         
33969         var queue = [];
33970         
33971         var boxes = [];
33972         
33973         var box = [];
33974         
33975         Roo.each(items, function(item, k){
33976             
33977             switch (item.size) {
33978                 case 'md' :
33979                 case 'md-left' :
33980                 case 'md-right' :
33981                 case 'tall' :
33982                     
33983                     if(box.length){
33984                         boxes.push(box);
33985                         box = [];
33986                     }
33987                     
33988                     boxes.push([item]);
33989                     
33990                     break;
33991                     
33992                 case 'xs' :
33993                 case 'sm' :
33994                 case 'wide' :
33995                 case 'wide-thin' :
33996                     
33997                     box.push(item);
33998                     
33999                     break;
34000                 default :
34001                     break;
34002                     
34003             }
34004             
34005         }, this);
34006         
34007         if(box.length){
34008             boxes.push(box);
34009             box = [];
34010         }
34011         
34012         var filterPattern = function(box, length)
34013         {
34014             if(!box.length){
34015                 return;
34016             }
34017             
34018             var match = false;
34019             
34020             var pattern = box.slice(0, length);
34021             
34022             var format = [];
34023             
34024             Roo.each(pattern, function(i){
34025                 format.push(i.size);
34026             }, this);
34027             
34028             Roo.each(standard, function(s){
34029                 
34030                 if(String(s) != String(format)){
34031                     return;
34032                 }
34033                 
34034                 match = true;
34035                 return false;
34036                 
34037             }, this);
34038             
34039             if(!match && length == 1){
34040                 return;
34041             }
34042             
34043             if(!match){
34044                 filterPattern(box, length - 1);
34045                 return;
34046             }
34047                 
34048             queue.push(pattern);
34049
34050             box = box.slice(length, box.length);
34051
34052             filterPattern(box, 4);
34053
34054             return;
34055             
34056         }
34057         
34058         Roo.each(boxes, function(box, k){
34059             
34060             if(!box.length){
34061                 return;
34062             }
34063             
34064             if(box.length == 1){
34065                 queue.push(box);
34066                 return;
34067             }
34068             
34069             filterPattern(box, 4);
34070             
34071         }, this);
34072         
34073         
34074         var prune = [];
34075         
34076         var pos = this.el.getBox(true);
34077         
34078         var minX = pos.x;
34079         
34080         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34081         
34082         var hit_end = false;
34083         
34084         Roo.each(queue, function(box){
34085             
34086             if(hit_end){
34087                 
34088                 Roo.each(box, function(b){
34089                 
34090                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34091                     b.el.hide();
34092
34093                 }, this);
34094
34095                 return;
34096             }
34097             
34098             var mx = 0;
34099             
34100             Roo.each(box, function(b){
34101                 
34102                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34103                 b.el.show();
34104
34105                 mx = Math.max(mx, b.x);
34106                 
34107             }, this);
34108             
34109             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34110             
34111             if(maxX < minX){
34112                 
34113                 Roo.each(box, function(b){
34114                 
34115                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34116                     b.el.hide();
34117                     
34118                 }, this);
34119                 
34120                 hit_end = true;
34121                 
34122                 return;
34123             }
34124             
34125             prune.push(box);
34126             
34127         }, this);
34128         
34129         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34130     },
34131     
34132     /** Sets position of item in DOM
34133     * @param {Element} item
34134     * @param {Number} x - horizontal position
34135     * @param {Number} y - vertical position
34136     * @param {Boolean} isInstant - disables transitions
34137     */
34138     _processVerticalLayoutQueue : function( queue, isInstant )
34139     {
34140         var pos = this.el.getBox(true);
34141         var x = pos.x;
34142         var y = pos.y;
34143         var maxY = [];
34144         
34145         for (var i = 0; i < this.cols; i++){
34146             maxY[i] = pos.y;
34147         }
34148         
34149         Roo.each(queue, function(box, k){
34150             
34151             var col = k % this.cols;
34152             
34153             Roo.each(box, function(b,kk){
34154                 
34155                 b.el.position('absolute');
34156                 
34157                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34158                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34159                 
34160                 if(b.size == 'md-left' || b.size == 'md-right'){
34161                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34162                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34163                 }
34164                 
34165                 b.el.setWidth(width);
34166                 b.el.setHeight(height);
34167                 // iframe?
34168                 b.el.select('iframe',true).setSize(width,height);
34169                 
34170             }, this);
34171             
34172             for (var i = 0; i < this.cols; i++){
34173                 
34174                 if(maxY[i] < maxY[col]){
34175                     col = i;
34176                     continue;
34177                 }
34178                 
34179                 col = Math.min(col, i);
34180                 
34181             }
34182             
34183             x = pos.x + col * (this.colWidth + this.padWidth);
34184             
34185             y = maxY[col];
34186             
34187             var positions = [];
34188             
34189             switch (box.length){
34190                 case 1 :
34191                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34192                     break;
34193                 case 2 :
34194                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34195                     break;
34196                 case 3 :
34197                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34198                     break;
34199                 case 4 :
34200                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34201                     break;
34202                 default :
34203                     break;
34204             }
34205             
34206             Roo.each(box, function(b,kk){
34207                 
34208                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34209                 
34210                 var sz = b.el.getSize();
34211                 
34212                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34213                 
34214             }, this);
34215             
34216         }, this);
34217         
34218         var mY = 0;
34219         
34220         for (var i = 0; i < this.cols; i++){
34221             mY = Math.max(mY, maxY[i]);
34222         }
34223         
34224         this.el.setHeight(mY - pos.y);
34225         
34226     },
34227     
34228 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34229 //    {
34230 //        var pos = this.el.getBox(true);
34231 //        var x = pos.x;
34232 //        var y = pos.y;
34233 //        var maxX = pos.right;
34234 //        
34235 //        var maxHeight = 0;
34236 //        
34237 //        Roo.each(items, function(item, k){
34238 //            
34239 //            var c = k % 2;
34240 //            
34241 //            item.el.position('absolute');
34242 //                
34243 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34244 //
34245 //            item.el.setWidth(width);
34246 //
34247 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34248 //
34249 //            item.el.setHeight(height);
34250 //            
34251 //            if(c == 0){
34252 //                item.el.setXY([x, y], isInstant ? false : true);
34253 //            } else {
34254 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34255 //            }
34256 //            
34257 //            y = y + height + this.alternativePadWidth;
34258 //            
34259 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34260 //            
34261 //        }, this);
34262 //        
34263 //        this.el.setHeight(maxHeight);
34264 //        
34265 //    },
34266     
34267     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34268     {
34269         var pos = this.el.getBox(true);
34270         
34271         var minX = pos.x;
34272         var minY = pos.y;
34273         
34274         var maxX = pos.right;
34275         
34276         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34277         
34278         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34279         
34280         Roo.each(queue, function(box, k){
34281             
34282             Roo.each(box, function(b, kk){
34283                 
34284                 b.el.position('absolute');
34285                 
34286                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34287                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34288                 
34289                 if(b.size == 'md-left' || b.size == 'md-right'){
34290                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34291                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34292                 }
34293                 
34294                 b.el.setWidth(width);
34295                 b.el.setHeight(height);
34296                 
34297             }, this);
34298             
34299             if(!box.length){
34300                 return;
34301             }
34302             
34303             var positions = [];
34304             
34305             switch (box.length){
34306                 case 1 :
34307                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34308                     break;
34309                 case 2 :
34310                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34311                     break;
34312                 case 3 :
34313                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34314                     break;
34315                 case 4 :
34316                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34317                     break;
34318                 default :
34319                     break;
34320             }
34321             
34322             Roo.each(box, function(b,kk){
34323                 
34324                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34325                 
34326                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34327                 
34328             }, this);
34329             
34330         }, this);
34331         
34332     },
34333     
34334     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34335     {
34336         Roo.each(eItems, function(b,k){
34337             
34338             b.size = (k == 0) ? 'sm' : 'xs';
34339             b.x = (k == 0) ? 2 : 1;
34340             b.y = (k == 0) ? 2 : 1;
34341             
34342             b.el.position('absolute');
34343             
34344             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34345                 
34346             b.el.setWidth(width);
34347             
34348             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34349             
34350             b.el.setHeight(height);
34351             
34352         }, this);
34353
34354         var positions = [];
34355         
34356         positions.push({
34357             x : maxX - this.unitWidth * 2 - this.gutter,
34358             y : minY
34359         });
34360         
34361         positions.push({
34362             x : maxX - this.unitWidth,
34363             y : minY + (this.unitWidth + this.gutter) * 2
34364         });
34365         
34366         positions.push({
34367             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34368             y : minY
34369         });
34370         
34371         Roo.each(eItems, function(b,k){
34372             
34373             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34374
34375         }, this);
34376         
34377     },
34378     
34379     getVerticalOneBoxColPositions : function(x, y, box)
34380     {
34381         var pos = [];
34382         
34383         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34384         
34385         if(box[0].size == 'md-left'){
34386             rand = 0;
34387         }
34388         
34389         if(box[0].size == 'md-right'){
34390             rand = 1;
34391         }
34392         
34393         pos.push({
34394             x : x + (this.unitWidth + this.gutter) * rand,
34395             y : y
34396         });
34397         
34398         return pos;
34399     },
34400     
34401     getVerticalTwoBoxColPositions : function(x, y, box)
34402     {
34403         var pos = [];
34404         
34405         if(box[0].size == 'xs'){
34406             
34407             pos.push({
34408                 x : x,
34409                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34410             });
34411
34412             pos.push({
34413                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34414                 y : y
34415             });
34416             
34417             return pos;
34418             
34419         }
34420         
34421         pos.push({
34422             x : x,
34423             y : y
34424         });
34425
34426         pos.push({
34427             x : x + (this.unitWidth + this.gutter) * 2,
34428             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34429         });
34430         
34431         return pos;
34432         
34433     },
34434     
34435     getVerticalThreeBoxColPositions : function(x, y, box)
34436     {
34437         var pos = [];
34438         
34439         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34440             
34441             pos.push({
34442                 x : x,
34443                 y : y
34444             });
34445
34446             pos.push({
34447                 x : x + (this.unitWidth + this.gutter) * 1,
34448                 y : y
34449             });
34450             
34451             pos.push({
34452                 x : x + (this.unitWidth + this.gutter) * 2,
34453                 y : y
34454             });
34455             
34456             return pos;
34457             
34458         }
34459         
34460         if(box[0].size == 'xs' && box[1].size == 'xs'){
34461             
34462             pos.push({
34463                 x : x,
34464                 y : y
34465             });
34466
34467             pos.push({
34468                 x : x,
34469                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34470             });
34471             
34472             pos.push({
34473                 x : x + (this.unitWidth + this.gutter) * 1,
34474                 y : y
34475             });
34476             
34477             return pos;
34478             
34479         }
34480         
34481         pos.push({
34482             x : x,
34483             y : y
34484         });
34485
34486         pos.push({
34487             x : x + (this.unitWidth + this.gutter) * 2,
34488             y : y
34489         });
34490
34491         pos.push({
34492             x : x + (this.unitWidth + this.gutter) * 2,
34493             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34494         });
34495             
34496         return pos;
34497         
34498     },
34499     
34500     getVerticalFourBoxColPositions : function(x, y, box)
34501     {
34502         var pos = [];
34503         
34504         if(box[0].size == 'xs'){
34505             
34506             pos.push({
34507                 x : x,
34508                 y : y
34509             });
34510
34511             pos.push({
34512                 x : x,
34513                 y : y + (this.unitHeight + this.gutter) * 1
34514             });
34515             
34516             pos.push({
34517                 x : x,
34518                 y : y + (this.unitHeight + this.gutter) * 2
34519             });
34520             
34521             pos.push({
34522                 x : x + (this.unitWidth + this.gutter) * 1,
34523                 y : y
34524             });
34525             
34526             return pos;
34527             
34528         }
34529         
34530         pos.push({
34531             x : x,
34532             y : y
34533         });
34534
34535         pos.push({
34536             x : x + (this.unitWidth + this.gutter) * 2,
34537             y : y
34538         });
34539
34540         pos.push({
34541             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34542             y : y + (this.unitHeight + this.gutter) * 1
34543         });
34544
34545         pos.push({
34546             x : x + (this.unitWidth + this.gutter) * 2,
34547             y : y + (this.unitWidth + this.gutter) * 2
34548         });
34549
34550         return pos;
34551         
34552     },
34553     
34554     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34555     {
34556         var pos = [];
34557         
34558         if(box[0].size == 'md-left'){
34559             pos.push({
34560                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34561                 y : minY
34562             });
34563             
34564             return pos;
34565         }
34566         
34567         if(box[0].size == 'md-right'){
34568             pos.push({
34569                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34570                 y : minY + (this.unitWidth + this.gutter) * 1
34571             });
34572             
34573             return pos;
34574         }
34575         
34576         var rand = Math.floor(Math.random() * (4 - box[0].y));
34577         
34578         pos.push({
34579             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34580             y : minY + (this.unitWidth + this.gutter) * rand
34581         });
34582         
34583         return pos;
34584         
34585     },
34586     
34587     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34588     {
34589         var pos = [];
34590         
34591         if(box[0].size == 'xs'){
34592             
34593             pos.push({
34594                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34595                 y : minY
34596             });
34597
34598             pos.push({
34599                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34600                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34601             });
34602             
34603             return pos;
34604             
34605         }
34606         
34607         pos.push({
34608             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34609             y : minY
34610         });
34611
34612         pos.push({
34613             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34614             y : minY + (this.unitWidth + this.gutter) * 2
34615         });
34616         
34617         return pos;
34618         
34619     },
34620     
34621     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34622     {
34623         var pos = [];
34624         
34625         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34626             
34627             pos.push({
34628                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34629                 y : minY
34630             });
34631
34632             pos.push({
34633                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34634                 y : minY + (this.unitWidth + this.gutter) * 1
34635             });
34636             
34637             pos.push({
34638                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34639                 y : minY + (this.unitWidth + this.gutter) * 2
34640             });
34641             
34642             return pos;
34643             
34644         }
34645         
34646         if(box[0].size == 'xs' && box[1].size == 'xs'){
34647             
34648             pos.push({
34649                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34650                 y : minY
34651             });
34652
34653             pos.push({
34654                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34655                 y : minY
34656             });
34657             
34658             pos.push({
34659                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34660                 y : minY + (this.unitWidth + this.gutter) * 1
34661             });
34662             
34663             return pos;
34664             
34665         }
34666         
34667         pos.push({
34668             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34669             y : minY
34670         });
34671
34672         pos.push({
34673             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34674             y : minY + (this.unitWidth + this.gutter) * 2
34675         });
34676
34677         pos.push({
34678             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34679             y : minY + (this.unitWidth + this.gutter) * 2
34680         });
34681             
34682         return pos;
34683         
34684     },
34685     
34686     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34687     {
34688         var pos = [];
34689         
34690         if(box[0].size == 'xs'){
34691             
34692             pos.push({
34693                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34694                 y : minY
34695             });
34696
34697             pos.push({
34698                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34699                 y : minY
34700             });
34701             
34702             pos.push({
34703                 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),
34704                 y : minY
34705             });
34706             
34707             pos.push({
34708                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34709                 y : minY + (this.unitWidth + this.gutter) * 1
34710             });
34711             
34712             return pos;
34713             
34714         }
34715         
34716         pos.push({
34717             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34718             y : minY
34719         });
34720         
34721         pos.push({
34722             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34723             y : minY + (this.unitWidth + this.gutter) * 2
34724         });
34725         
34726         pos.push({
34727             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34728             y : minY + (this.unitWidth + this.gutter) * 2
34729         });
34730         
34731         pos.push({
34732             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),
34733             y : minY + (this.unitWidth + this.gutter) * 2
34734         });
34735
34736         return pos;
34737         
34738     },
34739     
34740     /**
34741     * remove a Masonry Brick
34742     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34743     */
34744     removeBrick : function(brick_id)
34745     {
34746         if (!brick_id) {
34747             return;
34748         }
34749         
34750         for (var i = 0; i<this.bricks.length; i++) {
34751             if (this.bricks[i].id == brick_id) {
34752                 this.bricks.splice(i,1);
34753                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34754                 this.initial();
34755             }
34756         }
34757     },
34758     
34759     /**
34760     * adds a Masonry Brick
34761     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34762     */
34763     addBrick : function(cfg)
34764     {
34765         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34766         //this.register(cn);
34767         cn.parentId = this.id;
34768         cn.render(this.el);
34769         return cn;
34770     },
34771     
34772     /**
34773     * register a Masonry Brick
34774     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34775     */
34776     
34777     register : function(brick)
34778     {
34779         this.bricks.push(brick);
34780         brick.masonryId = this.id;
34781     },
34782     
34783     /**
34784     * clear all the Masonry Brick
34785     */
34786     clearAll : function()
34787     {
34788         this.bricks = [];
34789         //this.getChildContainer().dom.innerHTML = "";
34790         this.el.dom.innerHTML = '';
34791     },
34792     
34793     getSelected : function()
34794     {
34795         if (!this.selectedBrick) {
34796             return false;
34797         }
34798         
34799         return this.selectedBrick;
34800     }
34801 });
34802
34803 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34804     
34805     groups: {},
34806      /**
34807     * register a Masonry Layout
34808     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34809     */
34810     
34811     register : function(layout)
34812     {
34813         this.groups[layout.id] = layout;
34814     },
34815     /**
34816     * fetch a  Masonry Layout based on the masonry layout ID
34817     * @param {string} the masonry layout to add
34818     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34819     */
34820     
34821     get: function(layout_id) {
34822         if (typeof(this.groups[layout_id]) == 'undefined') {
34823             return false;
34824         }
34825         return this.groups[layout_id] ;
34826     }
34827     
34828     
34829     
34830 });
34831
34832  
34833
34834  /**
34835  *
34836  * This is based on 
34837  * http://masonry.desandro.com
34838  *
34839  * The idea is to render all the bricks based on vertical width...
34840  *
34841  * The original code extends 'outlayer' - we might need to use that....
34842  * 
34843  */
34844
34845
34846 /**
34847  * @class Roo.bootstrap.LayoutMasonryAuto
34848  * @extends Roo.bootstrap.Component
34849  * Bootstrap Layout Masonry class
34850  * 
34851  * @constructor
34852  * Create a new Element
34853  * @param {Object} config The config object
34854  */
34855
34856 Roo.bootstrap.LayoutMasonryAuto = function(config){
34857     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34858 };
34859
34860 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34861     
34862       /**
34863      * @cfg {Boolean} isFitWidth  - resize the width..
34864      */   
34865     isFitWidth : false,  // options..
34866     /**
34867      * @cfg {Boolean} isOriginLeft = left align?
34868      */   
34869     isOriginLeft : true,
34870     /**
34871      * @cfg {Boolean} isOriginTop = top align?
34872      */   
34873     isOriginTop : false,
34874     /**
34875      * @cfg {Boolean} isLayoutInstant = no animation?
34876      */   
34877     isLayoutInstant : false, // needed?
34878     /**
34879      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34880      */   
34881     isResizingContainer : true,
34882     /**
34883      * @cfg {Number} columnWidth  width of the columns 
34884      */   
34885     
34886     columnWidth : 0,
34887     
34888     /**
34889      * @cfg {Number} maxCols maximum number of columns
34890      */   
34891     
34892     maxCols: 0,
34893     /**
34894      * @cfg {Number} padHeight padding below box..
34895      */   
34896     
34897     padHeight : 10, 
34898     
34899     /**
34900      * @cfg {Boolean} isAutoInitial defalut true
34901      */   
34902     
34903     isAutoInitial : true, 
34904     
34905     // private?
34906     gutter : 0,
34907     
34908     containerWidth: 0,
34909     initialColumnWidth : 0,
34910     currentSize : null,
34911     
34912     colYs : null, // array.
34913     maxY : 0,
34914     padWidth: 10,
34915     
34916     
34917     tag: 'div',
34918     cls: '',
34919     bricks: null, //CompositeElement
34920     cols : 0, // array?
34921     // element : null, // wrapped now this.el
34922     _isLayoutInited : null, 
34923     
34924     
34925     getAutoCreate : function(){
34926         
34927         var cfg = {
34928             tag: this.tag,
34929             cls: 'blog-masonary-wrapper ' + this.cls,
34930             cn : {
34931                 cls : 'mas-boxes masonary'
34932             }
34933         };
34934         
34935         return cfg;
34936     },
34937     
34938     getChildContainer: function( )
34939     {
34940         if (this.boxesEl) {
34941             return this.boxesEl;
34942         }
34943         
34944         this.boxesEl = this.el.select('.mas-boxes').first();
34945         
34946         return this.boxesEl;
34947     },
34948     
34949     
34950     initEvents : function()
34951     {
34952         var _this = this;
34953         
34954         if(this.isAutoInitial){
34955             Roo.log('hook children rendered');
34956             this.on('childrenrendered', function() {
34957                 Roo.log('children rendered');
34958                 _this.initial();
34959             } ,this);
34960         }
34961         
34962     },
34963     
34964     initial : function()
34965     {
34966         this.reloadItems();
34967
34968         this.currentSize = this.el.getBox(true);
34969
34970         /// was window resize... - let's see if this works..
34971         Roo.EventManager.onWindowResize(this.resize, this); 
34972
34973         if(!this.isAutoInitial){
34974             this.layout();
34975             return;
34976         }
34977         
34978         this.layout.defer(500,this);
34979     },
34980     
34981     reloadItems: function()
34982     {
34983         this.bricks = this.el.select('.masonry-brick', true);
34984         
34985         this.bricks.each(function(b) {
34986             //Roo.log(b.getSize());
34987             if (!b.attr('originalwidth')) {
34988                 b.attr('originalwidth',  b.getSize().width);
34989             }
34990             
34991         });
34992         
34993         Roo.log(this.bricks.elements.length);
34994     },
34995     
34996     resize : function()
34997     {
34998         Roo.log('resize');
34999         var cs = this.el.getBox(true);
35000         
35001         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35002             Roo.log("no change in with or X");
35003             return;
35004         }
35005         this.currentSize = cs;
35006         this.layout();
35007     },
35008     
35009     layout : function()
35010     {
35011          Roo.log('layout');
35012         this._resetLayout();
35013         //this._manageStamps();
35014       
35015         // don't animate first layout
35016         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35017         this.layoutItems( isInstant );
35018       
35019         // flag for initalized
35020         this._isLayoutInited = true;
35021     },
35022     
35023     layoutItems : function( isInstant )
35024     {
35025         //var items = this._getItemsForLayout( this.items );
35026         // original code supports filtering layout items.. we just ignore it..
35027         
35028         this._layoutItems( this.bricks , isInstant );
35029       
35030         this._postLayout();
35031     },
35032     _layoutItems : function ( items , isInstant)
35033     {
35034        //this.fireEvent( 'layout', this, items );
35035     
35036
35037         if ( !items || !items.elements.length ) {
35038           // no items, emit event with empty array
35039             return;
35040         }
35041
35042         var queue = [];
35043         items.each(function(item) {
35044             Roo.log("layout item");
35045             Roo.log(item);
35046             // get x/y object from method
35047             var position = this._getItemLayoutPosition( item );
35048             // enqueue
35049             position.item = item;
35050             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35051             queue.push( position );
35052         }, this);
35053       
35054         this._processLayoutQueue( queue );
35055     },
35056     /** Sets position of item in DOM
35057     * @param {Element} item
35058     * @param {Number} x - horizontal position
35059     * @param {Number} y - vertical position
35060     * @param {Boolean} isInstant - disables transitions
35061     */
35062     _processLayoutQueue : function( queue )
35063     {
35064         for ( var i=0, len = queue.length; i < len; i++ ) {
35065             var obj = queue[i];
35066             obj.item.position('absolute');
35067             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35068         }
35069     },
35070       
35071     
35072     /**
35073     * Any logic you want to do after each layout,
35074     * i.e. size the container
35075     */
35076     _postLayout : function()
35077     {
35078         this.resizeContainer();
35079     },
35080     
35081     resizeContainer : function()
35082     {
35083         if ( !this.isResizingContainer ) {
35084             return;
35085         }
35086         var size = this._getContainerSize();
35087         if ( size ) {
35088             this.el.setSize(size.width,size.height);
35089             this.boxesEl.setSize(size.width,size.height);
35090         }
35091     },
35092     
35093     
35094     
35095     _resetLayout : function()
35096     {
35097         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35098         this.colWidth = this.el.getWidth();
35099         //this.gutter = this.el.getWidth(); 
35100         
35101         this.measureColumns();
35102
35103         // reset column Y
35104         var i = this.cols;
35105         this.colYs = [];
35106         while (i--) {
35107             this.colYs.push( 0 );
35108         }
35109     
35110         this.maxY = 0;
35111     },
35112
35113     measureColumns : function()
35114     {
35115         this.getContainerWidth();
35116       // if columnWidth is 0, default to outerWidth of first item
35117         if ( !this.columnWidth ) {
35118             var firstItem = this.bricks.first();
35119             Roo.log(firstItem);
35120             this.columnWidth  = this.containerWidth;
35121             if (firstItem && firstItem.attr('originalwidth') ) {
35122                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35123             }
35124             // columnWidth fall back to item of first element
35125             Roo.log("set column width?");
35126                         this.initialColumnWidth = this.columnWidth  ;
35127
35128             // if first elem has no width, default to size of container
35129             
35130         }
35131         
35132         
35133         if (this.initialColumnWidth) {
35134             this.columnWidth = this.initialColumnWidth;
35135         }
35136         
35137         
35138             
35139         // column width is fixed at the top - however if container width get's smaller we should
35140         // reduce it...
35141         
35142         // this bit calcs how man columns..
35143             
35144         var columnWidth = this.columnWidth += this.gutter;
35145       
35146         // calculate columns
35147         var containerWidth = this.containerWidth + this.gutter;
35148         
35149         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35150         // fix rounding errors, typically with gutters
35151         var excess = columnWidth - containerWidth % columnWidth;
35152         
35153         
35154         // if overshoot is less than a pixel, round up, otherwise floor it
35155         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35156         cols = Math[ mathMethod ]( cols );
35157         this.cols = Math.max( cols, 1 );
35158         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35159         
35160          // padding positioning..
35161         var totalColWidth = this.cols * this.columnWidth;
35162         var padavail = this.containerWidth - totalColWidth;
35163         // so for 2 columns - we need 3 'pads'
35164         
35165         var padNeeded = (1+this.cols) * this.padWidth;
35166         
35167         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35168         
35169         this.columnWidth += padExtra
35170         //this.padWidth = Math.floor(padavail /  ( this.cols));
35171         
35172         // adjust colum width so that padding is fixed??
35173         
35174         // we have 3 columns ... total = width * 3
35175         // we have X left over... that should be used by 
35176         
35177         //if (this.expandC) {
35178             
35179         //}
35180         
35181         
35182         
35183     },
35184     
35185     getContainerWidth : function()
35186     {
35187        /* // container is parent if fit width
35188         var container = this.isFitWidth ? this.element.parentNode : this.element;
35189         // check that this.size and size are there
35190         // IE8 triggers resize on body size change, so they might not be
35191         
35192         var size = getSize( container );  //FIXME
35193         this.containerWidth = size && size.innerWidth; //FIXME
35194         */
35195          
35196         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35197         
35198     },
35199     
35200     _getItemLayoutPosition : function( item )  // what is item?
35201     {
35202         // we resize the item to our columnWidth..
35203       
35204         item.setWidth(this.columnWidth);
35205         item.autoBoxAdjust  = false;
35206         
35207         var sz = item.getSize();
35208  
35209         // how many columns does this brick span
35210         var remainder = this.containerWidth % this.columnWidth;
35211         
35212         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35213         // round if off by 1 pixel, otherwise use ceil
35214         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35215         colSpan = Math.min( colSpan, this.cols );
35216         
35217         // normally this should be '1' as we dont' currently allow multi width columns..
35218         
35219         var colGroup = this._getColGroup( colSpan );
35220         // get the minimum Y value from the columns
35221         var minimumY = Math.min.apply( Math, colGroup );
35222         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35223         
35224         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35225          
35226         // position the brick
35227         var position = {
35228             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35229             y: this.currentSize.y + minimumY + this.padHeight
35230         };
35231         
35232         Roo.log(position);
35233         // apply setHeight to necessary columns
35234         var setHeight = minimumY + sz.height + this.padHeight;
35235         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35236         
35237         var setSpan = this.cols + 1 - colGroup.length;
35238         for ( var i = 0; i < setSpan; i++ ) {
35239           this.colYs[ shortColIndex + i ] = setHeight ;
35240         }
35241       
35242         return position;
35243     },
35244     
35245     /**
35246      * @param {Number} colSpan - number of columns the element spans
35247      * @returns {Array} colGroup
35248      */
35249     _getColGroup : function( colSpan )
35250     {
35251         if ( colSpan < 2 ) {
35252           // if brick spans only one column, use all the column Ys
35253           return this.colYs;
35254         }
35255       
35256         var colGroup = [];
35257         // how many different places could this brick fit horizontally
35258         var groupCount = this.cols + 1 - colSpan;
35259         // for each group potential horizontal position
35260         for ( var i = 0; i < groupCount; i++ ) {
35261           // make an array of colY values for that one group
35262           var groupColYs = this.colYs.slice( i, i + colSpan );
35263           // and get the max value of the array
35264           colGroup[i] = Math.max.apply( Math, groupColYs );
35265         }
35266         return colGroup;
35267     },
35268     /*
35269     _manageStamp : function( stamp )
35270     {
35271         var stampSize =  stamp.getSize();
35272         var offset = stamp.getBox();
35273         // get the columns that this stamp affects
35274         var firstX = this.isOriginLeft ? offset.x : offset.right;
35275         var lastX = firstX + stampSize.width;
35276         var firstCol = Math.floor( firstX / this.columnWidth );
35277         firstCol = Math.max( 0, firstCol );
35278         
35279         var lastCol = Math.floor( lastX / this.columnWidth );
35280         // lastCol should not go over if multiple of columnWidth #425
35281         lastCol -= lastX % this.columnWidth ? 0 : 1;
35282         lastCol = Math.min( this.cols - 1, lastCol );
35283         
35284         // set colYs to bottom of the stamp
35285         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35286             stampSize.height;
35287             
35288         for ( var i = firstCol; i <= lastCol; i++ ) {
35289           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35290         }
35291     },
35292     */
35293     
35294     _getContainerSize : function()
35295     {
35296         this.maxY = Math.max.apply( Math, this.colYs );
35297         var size = {
35298             height: this.maxY
35299         };
35300       
35301         if ( this.isFitWidth ) {
35302             size.width = this._getContainerFitWidth();
35303         }
35304       
35305         return size;
35306     },
35307     
35308     _getContainerFitWidth : function()
35309     {
35310         var unusedCols = 0;
35311         // count unused columns
35312         var i = this.cols;
35313         while ( --i ) {
35314           if ( this.colYs[i] !== 0 ) {
35315             break;
35316           }
35317           unusedCols++;
35318         }
35319         // fit container to columns that have been used
35320         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35321     },
35322     
35323     needsResizeLayout : function()
35324     {
35325         var previousWidth = this.containerWidth;
35326         this.getContainerWidth();
35327         return previousWidth !== this.containerWidth;
35328     }
35329  
35330 });
35331
35332  
35333
35334  /*
35335  * - LGPL
35336  *
35337  * element
35338  * 
35339  */
35340
35341 /**
35342  * @class Roo.bootstrap.MasonryBrick
35343  * @extends Roo.bootstrap.Component
35344  * Bootstrap MasonryBrick class
35345  * 
35346  * @constructor
35347  * Create a new MasonryBrick
35348  * @param {Object} config The config object
35349  */
35350
35351 Roo.bootstrap.MasonryBrick = function(config){
35352     
35353     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35354     
35355     Roo.bootstrap.MasonryBrick.register(this);
35356     
35357     this.addEvents({
35358         // raw events
35359         /**
35360          * @event click
35361          * When a MasonryBrick is clcik
35362          * @param {Roo.bootstrap.MasonryBrick} this
35363          * @param {Roo.EventObject} e
35364          */
35365         "click" : true
35366     });
35367 };
35368
35369 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35370     
35371     /**
35372      * @cfg {String} title
35373      */   
35374     title : '',
35375     /**
35376      * @cfg {String} html
35377      */   
35378     html : '',
35379     /**
35380      * @cfg {String} bgimage
35381      */   
35382     bgimage : '',
35383     /**
35384      * @cfg {String} videourl
35385      */   
35386     videourl : '',
35387     /**
35388      * @cfg {String} cls
35389      */   
35390     cls : '',
35391     /**
35392      * @cfg {String} href
35393      */   
35394     href : '',
35395     /**
35396      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35397      */   
35398     size : 'xs',
35399     
35400     /**
35401      * @cfg {String} placetitle (center|bottom)
35402      */   
35403     placetitle : '',
35404     
35405     /**
35406      * @cfg {Boolean} isFitContainer defalut true
35407      */   
35408     isFitContainer : true, 
35409     
35410     /**
35411      * @cfg {Boolean} preventDefault defalut false
35412      */   
35413     preventDefault : false, 
35414     
35415     /**
35416      * @cfg {Boolean} inverse defalut false
35417      */   
35418     maskInverse : false, 
35419     
35420     getAutoCreate : function()
35421     {
35422         if(!this.isFitContainer){
35423             return this.getSplitAutoCreate();
35424         }
35425         
35426         var cls = 'masonry-brick masonry-brick-full';
35427         
35428         if(this.href.length){
35429             cls += ' masonry-brick-link';
35430         }
35431         
35432         if(this.bgimage.length){
35433             cls += ' masonry-brick-image';
35434         }
35435         
35436         if(this.maskInverse){
35437             cls += ' mask-inverse';
35438         }
35439         
35440         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35441             cls += ' enable-mask';
35442         }
35443         
35444         if(this.size){
35445             cls += ' masonry-' + this.size + '-brick';
35446         }
35447         
35448         if(this.placetitle.length){
35449             
35450             switch (this.placetitle) {
35451                 case 'center' :
35452                     cls += ' masonry-center-title';
35453                     break;
35454                 case 'bottom' :
35455                     cls += ' masonry-bottom-title';
35456                     break;
35457                 default:
35458                     break;
35459             }
35460             
35461         } else {
35462             if(!this.html.length && !this.bgimage.length){
35463                 cls += ' masonry-center-title';
35464             }
35465
35466             if(!this.html.length && this.bgimage.length){
35467                 cls += ' masonry-bottom-title';
35468             }
35469         }
35470         
35471         if(this.cls){
35472             cls += ' ' + this.cls;
35473         }
35474         
35475         var cfg = {
35476             tag: (this.href.length) ? 'a' : 'div',
35477             cls: cls,
35478             cn: [
35479                 {
35480                     tag: 'div',
35481                     cls: 'masonry-brick-mask'
35482                 },
35483                 {
35484                     tag: 'div',
35485                     cls: 'masonry-brick-paragraph',
35486                     cn: []
35487                 }
35488             ]
35489         };
35490         
35491         if(this.href.length){
35492             cfg.href = this.href;
35493         }
35494         
35495         var cn = cfg.cn[1].cn;
35496         
35497         if(this.title.length){
35498             cn.push({
35499                 tag: 'h4',
35500                 cls: 'masonry-brick-title',
35501                 html: this.title
35502             });
35503         }
35504         
35505         if(this.html.length){
35506             cn.push({
35507                 tag: 'p',
35508                 cls: 'masonry-brick-text',
35509                 html: this.html
35510             });
35511         }
35512         
35513         if (!this.title.length && !this.html.length) {
35514             cfg.cn[1].cls += ' hide';
35515         }
35516         
35517         if(this.bgimage.length){
35518             cfg.cn.push({
35519                 tag: 'img',
35520                 cls: 'masonry-brick-image-view',
35521                 src: this.bgimage
35522             });
35523         }
35524         
35525         if(this.videourl.length){
35526             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35527             // youtube support only?
35528             cfg.cn.push({
35529                 tag: 'iframe',
35530                 cls: 'masonry-brick-image-view',
35531                 src: vurl,
35532                 frameborder : 0,
35533                 allowfullscreen : true
35534             });
35535         }
35536         
35537         return cfg;
35538         
35539     },
35540     
35541     getSplitAutoCreate : function()
35542     {
35543         var cls = 'masonry-brick masonry-brick-split';
35544         
35545         if(this.href.length){
35546             cls += ' masonry-brick-link';
35547         }
35548         
35549         if(this.bgimage.length){
35550             cls += ' masonry-brick-image';
35551         }
35552         
35553         if(this.size){
35554             cls += ' masonry-' + this.size + '-brick';
35555         }
35556         
35557         switch (this.placetitle) {
35558             case 'center' :
35559                 cls += ' masonry-center-title';
35560                 break;
35561             case 'bottom' :
35562                 cls += ' masonry-bottom-title';
35563                 break;
35564             default:
35565                 if(!this.bgimage.length){
35566                     cls += ' masonry-center-title';
35567                 }
35568
35569                 if(this.bgimage.length){
35570                     cls += ' masonry-bottom-title';
35571                 }
35572                 break;
35573         }
35574         
35575         if(this.cls){
35576             cls += ' ' + this.cls;
35577         }
35578         
35579         var cfg = {
35580             tag: (this.href.length) ? 'a' : 'div',
35581             cls: cls,
35582             cn: [
35583                 {
35584                     tag: 'div',
35585                     cls: 'masonry-brick-split-head',
35586                     cn: [
35587                         {
35588                             tag: 'div',
35589                             cls: 'masonry-brick-paragraph',
35590                             cn: []
35591                         }
35592                     ]
35593                 },
35594                 {
35595                     tag: 'div',
35596                     cls: 'masonry-brick-split-body',
35597                     cn: []
35598                 }
35599             ]
35600         };
35601         
35602         if(this.href.length){
35603             cfg.href = this.href;
35604         }
35605         
35606         if(this.title.length){
35607             cfg.cn[0].cn[0].cn.push({
35608                 tag: 'h4',
35609                 cls: 'masonry-brick-title',
35610                 html: this.title
35611             });
35612         }
35613         
35614         if(this.html.length){
35615             cfg.cn[1].cn.push({
35616                 tag: 'p',
35617                 cls: 'masonry-brick-text',
35618                 html: this.html
35619             });
35620         }
35621
35622         if(this.bgimage.length){
35623             cfg.cn[0].cn.push({
35624                 tag: 'img',
35625                 cls: 'masonry-brick-image-view',
35626                 src: this.bgimage
35627             });
35628         }
35629         
35630         if(this.videourl.length){
35631             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35632             // youtube support only?
35633             cfg.cn[0].cn.cn.push({
35634                 tag: 'iframe',
35635                 cls: 'masonry-brick-image-view',
35636                 src: vurl,
35637                 frameborder : 0,
35638                 allowfullscreen : true
35639             });
35640         }
35641         
35642         return cfg;
35643     },
35644     
35645     initEvents: function() 
35646     {
35647         switch (this.size) {
35648             case 'xs' :
35649                 this.x = 1;
35650                 this.y = 1;
35651                 break;
35652             case 'sm' :
35653                 this.x = 2;
35654                 this.y = 2;
35655                 break;
35656             case 'md' :
35657             case 'md-left' :
35658             case 'md-right' :
35659                 this.x = 3;
35660                 this.y = 3;
35661                 break;
35662             case 'tall' :
35663                 this.x = 2;
35664                 this.y = 3;
35665                 break;
35666             case 'wide' :
35667                 this.x = 3;
35668                 this.y = 2;
35669                 break;
35670             case 'wide-thin' :
35671                 this.x = 3;
35672                 this.y = 1;
35673                 break;
35674                         
35675             default :
35676                 break;
35677         }
35678         
35679         if(Roo.isTouch){
35680             this.el.on('touchstart', this.onTouchStart, this);
35681             this.el.on('touchmove', this.onTouchMove, this);
35682             this.el.on('touchend', this.onTouchEnd, this);
35683             this.el.on('contextmenu', this.onContextMenu, this);
35684         } else {
35685             this.el.on('mouseenter'  ,this.enter, this);
35686             this.el.on('mouseleave', this.leave, this);
35687             this.el.on('click', this.onClick, this);
35688         }
35689         
35690         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35691             this.parent().bricks.push(this);   
35692         }
35693         
35694     },
35695     
35696     onClick: function(e, el)
35697     {
35698         var time = this.endTimer - this.startTimer;
35699         // Roo.log(e.preventDefault());
35700         if(Roo.isTouch){
35701             if(time > 1000){
35702                 e.preventDefault();
35703                 return;
35704             }
35705         }
35706         
35707         if(!this.preventDefault){
35708             return;
35709         }
35710         
35711         e.preventDefault();
35712         
35713         if (this.activeClass != '') {
35714             this.selectBrick();
35715         }
35716         
35717         this.fireEvent('click', this, e);
35718     },
35719     
35720     enter: function(e, el)
35721     {
35722         e.preventDefault();
35723         
35724         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35725             return;
35726         }
35727         
35728         if(this.bgimage.length && this.html.length){
35729             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35730         }
35731     },
35732     
35733     leave: function(e, el)
35734     {
35735         e.preventDefault();
35736         
35737         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35738             return;
35739         }
35740         
35741         if(this.bgimage.length && this.html.length){
35742             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35743         }
35744     },
35745     
35746     onTouchStart: function(e, el)
35747     {
35748 //        e.preventDefault();
35749         
35750         this.touchmoved = false;
35751         
35752         if(!this.isFitContainer){
35753             return;
35754         }
35755         
35756         if(!this.bgimage.length || !this.html.length){
35757             return;
35758         }
35759         
35760         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35761         
35762         this.timer = new Date().getTime();
35763         
35764     },
35765     
35766     onTouchMove: function(e, el)
35767     {
35768         this.touchmoved = true;
35769     },
35770     
35771     onContextMenu : function(e,el)
35772     {
35773         e.preventDefault();
35774         e.stopPropagation();
35775         return false;
35776     },
35777     
35778     onTouchEnd: function(e, el)
35779     {
35780 //        e.preventDefault();
35781         
35782         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35783         
35784             this.leave(e,el);
35785             
35786             return;
35787         }
35788         
35789         if(!this.bgimage.length || !this.html.length){
35790             
35791             if(this.href.length){
35792                 window.location.href = this.href;
35793             }
35794             
35795             return;
35796         }
35797         
35798         if(!this.isFitContainer){
35799             return;
35800         }
35801         
35802         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35803         
35804         window.location.href = this.href;
35805     },
35806     
35807     //selection on single brick only
35808     selectBrick : function() {
35809         
35810         if (!this.parentId) {
35811             return;
35812         }
35813         
35814         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35815         var index = m.selectedBrick.indexOf(this.id);
35816         
35817         if ( index > -1) {
35818             m.selectedBrick.splice(index,1);
35819             this.el.removeClass(this.activeClass);
35820             return;
35821         }
35822         
35823         for(var i = 0; i < m.selectedBrick.length; i++) {
35824             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35825             b.el.removeClass(b.activeClass);
35826         }
35827         
35828         m.selectedBrick = [];
35829         
35830         m.selectedBrick.push(this.id);
35831         this.el.addClass(this.activeClass);
35832         return;
35833     },
35834     
35835     isSelected : function(){
35836         return this.el.hasClass(this.activeClass);
35837         
35838     }
35839 });
35840
35841 Roo.apply(Roo.bootstrap.MasonryBrick, {
35842     
35843     //groups: {},
35844     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35845      /**
35846     * register a Masonry Brick
35847     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35848     */
35849     
35850     register : function(brick)
35851     {
35852         //this.groups[brick.id] = brick;
35853         this.groups.add(brick.id, brick);
35854     },
35855     /**
35856     * fetch a  masonry brick based on the masonry brick ID
35857     * @param {string} the masonry brick to add
35858     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35859     */
35860     
35861     get: function(brick_id) 
35862     {
35863         // if (typeof(this.groups[brick_id]) == 'undefined') {
35864         //     return false;
35865         // }
35866         // return this.groups[brick_id] ;
35867         
35868         if(this.groups.key(brick_id)) {
35869             return this.groups.key(brick_id);
35870         }
35871         
35872         return false;
35873     }
35874     
35875     
35876     
35877 });
35878
35879  /*
35880  * - LGPL
35881  *
35882  * element
35883  * 
35884  */
35885
35886 /**
35887  * @class Roo.bootstrap.Brick
35888  * @extends Roo.bootstrap.Component
35889  * Bootstrap Brick class
35890  * 
35891  * @constructor
35892  * Create a new Brick
35893  * @param {Object} config The config object
35894  */
35895
35896 Roo.bootstrap.Brick = function(config){
35897     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35898     
35899     this.addEvents({
35900         // raw events
35901         /**
35902          * @event click
35903          * When a Brick is click
35904          * @param {Roo.bootstrap.Brick} this
35905          * @param {Roo.EventObject} e
35906          */
35907         "click" : true
35908     });
35909 };
35910
35911 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35912     
35913     /**
35914      * @cfg {String} title
35915      */   
35916     title : '',
35917     /**
35918      * @cfg {String} html
35919      */   
35920     html : '',
35921     /**
35922      * @cfg {String} bgimage
35923      */   
35924     bgimage : '',
35925     /**
35926      * @cfg {String} cls
35927      */   
35928     cls : '',
35929     /**
35930      * @cfg {String} href
35931      */   
35932     href : '',
35933     /**
35934      * @cfg {String} video
35935      */   
35936     video : '',
35937     /**
35938      * @cfg {Boolean} square
35939      */   
35940     square : true,
35941     
35942     getAutoCreate : function()
35943     {
35944         var cls = 'roo-brick';
35945         
35946         if(this.href.length){
35947             cls += ' roo-brick-link';
35948         }
35949         
35950         if(this.bgimage.length){
35951             cls += ' roo-brick-image';
35952         }
35953         
35954         if(!this.html.length && !this.bgimage.length){
35955             cls += ' roo-brick-center-title';
35956         }
35957         
35958         if(!this.html.length && this.bgimage.length){
35959             cls += ' roo-brick-bottom-title';
35960         }
35961         
35962         if(this.cls){
35963             cls += ' ' + this.cls;
35964         }
35965         
35966         var cfg = {
35967             tag: (this.href.length) ? 'a' : 'div',
35968             cls: cls,
35969             cn: [
35970                 {
35971                     tag: 'div',
35972                     cls: 'roo-brick-paragraph',
35973                     cn: []
35974                 }
35975             ]
35976         };
35977         
35978         if(this.href.length){
35979             cfg.href = this.href;
35980         }
35981         
35982         var cn = cfg.cn[0].cn;
35983         
35984         if(this.title.length){
35985             cn.push({
35986                 tag: 'h4',
35987                 cls: 'roo-brick-title',
35988                 html: this.title
35989             });
35990         }
35991         
35992         if(this.html.length){
35993             cn.push({
35994                 tag: 'p',
35995                 cls: 'roo-brick-text',
35996                 html: this.html
35997             });
35998         } else {
35999             cn.cls += ' hide';
36000         }
36001         
36002         if(this.bgimage.length){
36003             cfg.cn.push({
36004                 tag: 'img',
36005                 cls: 'roo-brick-image-view',
36006                 src: this.bgimage
36007             });
36008         }
36009         
36010         return cfg;
36011     },
36012     
36013     initEvents: function() 
36014     {
36015         if(this.title.length || this.html.length){
36016             this.el.on('mouseenter'  ,this.enter, this);
36017             this.el.on('mouseleave', this.leave, this);
36018         }
36019         
36020         Roo.EventManager.onWindowResize(this.resize, this); 
36021         
36022         if(this.bgimage.length){
36023             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36024             this.imageEl.on('load', this.onImageLoad, this);
36025             return;
36026         }
36027         
36028         this.resize();
36029     },
36030     
36031     onImageLoad : function()
36032     {
36033         this.resize();
36034     },
36035     
36036     resize : function()
36037     {
36038         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36039         
36040         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36041         
36042         if(this.bgimage.length){
36043             var image = this.el.select('.roo-brick-image-view', true).first();
36044             
36045             image.setWidth(paragraph.getWidth());
36046             
36047             if(this.square){
36048                 image.setHeight(paragraph.getWidth());
36049             }
36050             
36051             this.el.setHeight(image.getHeight());
36052             paragraph.setHeight(image.getHeight());
36053             
36054         }
36055         
36056     },
36057     
36058     enter: function(e, el)
36059     {
36060         e.preventDefault();
36061         
36062         if(this.bgimage.length){
36063             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36064             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36065         }
36066     },
36067     
36068     leave: function(e, el)
36069     {
36070         e.preventDefault();
36071         
36072         if(this.bgimage.length){
36073             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36074             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36075         }
36076     }
36077     
36078 });
36079
36080  
36081
36082  /*
36083  * - LGPL
36084  *
36085  * Number field 
36086  */
36087
36088 /**
36089  * @class Roo.bootstrap.NumberField
36090  * @extends Roo.bootstrap.Input
36091  * Bootstrap NumberField class
36092  * 
36093  * 
36094  * 
36095  * 
36096  * @constructor
36097  * Create a new NumberField
36098  * @param {Object} config The config object
36099  */
36100
36101 Roo.bootstrap.NumberField = function(config){
36102     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36103 };
36104
36105 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36106     
36107     /**
36108      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36109      */
36110     allowDecimals : true,
36111     /**
36112      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36113      */
36114     decimalSeparator : ".",
36115     /**
36116      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36117      */
36118     decimalPrecision : 2,
36119     /**
36120      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36121      */
36122     allowNegative : true,
36123     
36124     /**
36125      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36126      */
36127     allowZero: true,
36128     /**
36129      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36130      */
36131     minValue : Number.NEGATIVE_INFINITY,
36132     /**
36133      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36134      */
36135     maxValue : Number.MAX_VALUE,
36136     /**
36137      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36138      */
36139     minText : "The minimum value for this field is {0}",
36140     /**
36141      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36142      */
36143     maxText : "The maximum value for this field is {0}",
36144     /**
36145      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36146      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36147      */
36148     nanText : "{0} is not a valid number",
36149     /**
36150      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36151      */
36152     thousandsDelimiter : false,
36153     /**
36154      * @cfg {String} valueAlign alignment of value
36155      */
36156     valueAlign : "left",
36157
36158     getAutoCreate : function()
36159     {
36160         var hiddenInput = {
36161             tag: 'input',
36162             type: 'hidden',
36163             id: Roo.id(),
36164             cls: 'hidden-number-input'
36165         };
36166         
36167         if (this.name) {
36168             hiddenInput.name = this.name;
36169         }
36170         
36171         this.name = '';
36172         
36173         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36174         
36175         this.name = hiddenInput.name;
36176         
36177         if(cfg.cn.length > 0) {
36178             cfg.cn.push(hiddenInput);
36179         }
36180         
36181         return cfg;
36182     },
36183
36184     // private
36185     initEvents : function()
36186     {   
36187         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36188         
36189         var allowed = "0123456789";
36190         
36191         if(this.allowDecimals){
36192             allowed += this.decimalSeparator;
36193         }
36194         
36195         if(this.allowNegative){
36196             allowed += "-";
36197         }
36198         
36199         if(this.thousandsDelimiter) {
36200             allowed += ",";
36201         }
36202         
36203         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36204         
36205         var keyPress = function(e){
36206             
36207             var k = e.getKey();
36208             
36209             var c = e.getCharCode();
36210             
36211             if(
36212                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36213                     allowed.indexOf(String.fromCharCode(c)) === -1
36214             ){
36215                 e.stopEvent();
36216                 return;
36217             }
36218             
36219             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36220                 return;
36221             }
36222             
36223             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36224                 e.stopEvent();
36225             }
36226         };
36227         
36228         this.el.on("keypress", keyPress, this);
36229     },
36230     
36231     validateValue : function(value)
36232     {
36233         
36234         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36235             return false;
36236         }
36237         
36238         var num = this.parseValue(value);
36239         
36240         if(isNaN(num)){
36241             this.markInvalid(String.format(this.nanText, value));
36242             return false;
36243         }
36244         
36245         if(num < this.minValue){
36246             this.markInvalid(String.format(this.minText, this.minValue));
36247             return false;
36248         }
36249         
36250         if(num > this.maxValue){
36251             this.markInvalid(String.format(this.maxText, this.maxValue));
36252             return false;
36253         }
36254         
36255         return true;
36256     },
36257
36258     getValue : function()
36259     {
36260         var v = this.hiddenEl().getValue();
36261         
36262         return this.fixPrecision(this.parseValue(v));
36263     },
36264
36265     parseValue : function(value)
36266     {
36267         if(this.thousandsDelimiter) {
36268             value += "";
36269             r = new RegExp(",", "g");
36270             value = value.replace(r, "");
36271         }
36272         
36273         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36274         return isNaN(value) ? '' : value;
36275     },
36276
36277     fixPrecision : function(value)
36278     {
36279         if(this.thousandsDelimiter) {
36280             value += "";
36281             r = new RegExp(",", "g");
36282             value = value.replace(r, "");
36283         }
36284         
36285         var nan = isNaN(value);
36286         
36287         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36288             return nan ? '' : value;
36289         }
36290         return parseFloat(value).toFixed(this.decimalPrecision);
36291     },
36292
36293     setValue : function(v)
36294     {
36295         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36296         
36297         this.value = v;
36298         
36299         if(this.rendered){
36300             
36301             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36302             
36303             this.inputEl().dom.value = (v == '') ? '' :
36304                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36305             
36306             if(!this.allowZero && v === '0') {
36307                 this.hiddenEl().dom.value = '';
36308                 this.inputEl().dom.value = '';
36309             }
36310             
36311             this.validate();
36312         }
36313     },
36314
36315     decimalPrecisionFcn : function(v)
36316     {
36317         return Math.floor(v);
36318     },
36319
36320     beforeBlur : function()
36321     {
36322         var v = this.parseValue(this.getRawValue());
36323         
36324         if(v || v === 0 || v === ''){
36325             this.setValue(v);
36326         }
36327     },
36328     
36329     hiddenEl : function()
36330     {
36331         return this.el.select('input.hidden-number-input',true).first();
36332     }
36333     
36334 });
36335
36336  
36337
36338 /*
36339 * Licence: LGPL
36340 */
36341
36342 /**
36343  * @class Roo.bootstrap.DocumentSlider
36344  * @extends Roo.bootstrap.Component
36345  * Bootstrap DocumentSlider class
36346  * 
36347  * @constructor
36348  * Create a new DocumentViewer
36349  * @param {Object} config The config object
36350  */
36351
36352 Roo.bootstrap.DocumentSlider = function(config){
36353     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36354     
36355     this.files = [];
36356     
36357     this.addEvents({
36358         /**
36359          * @event initial
36360          * Fire after initEvent
36361          * @param {Roo.bootstrap.DocumentSlider} this
36362          */
36363         "initial" : true,
36364         /**
36365          * @event update
36366          * Fire after update
36367          * @param {Roo.bootstrap.DocumentSlider} this
36368          */
36369         "update" : true,
36370         /**
36371          * @event click
36372          * Fire after click
36373          * @param {Roo.bootstrap.DocumentSlider} this
36374          */
36375         "click" : true
36376     });
36377 };
36378
36379 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36380     
36381     files : false,
36382     
36383     indicator : 0,
36384     
36385     getAutoCreate : function()
36386     {
36387         var cfg = {
36388             tag : 'div',
36389             cls : 'roo-document-slider',
36390             cn : [
36391                 {
36392                     tag : 'div',
36393                     cls : 'roo-document-slider-header',
36394                     cn : [
36395                         {
36396                             tag : 'div',
36397                             cls : 'roo-document-slider-header-title'
36398                         }
36399                     ]
36400                 },
36401                 {
36402                     tag : 'div',
36403                     cls : 'roo-document-slider-body',
36404                     cn : [
36405                         {
36406                             tag : 'div',
36407                             cls : 'roo-document-slider-prev',
36408                             cn : [
36409                                 {
36410                                     tag : 'i',
36411                                     cls : 'fa fa-chevron-left'
36412                                 }
36413                             ]
36414                         },
36415                         {
36416                             tag : 'div',
36417                             cls : 'roo-document-slider-thumb',
36418                             cn : [
36419                                 {
36420                                     tag : 'img',
36421                                     cls : 'roo-document-slider-image'
36422                                 }
36423                             ]
36424                         },
36425                         {
36426                             tag : 'div',
36427                             cls : 'roo-document-slider-next',
36428                             cn : [
36429                                 {
36430                                     tag : 'i',
36431                                     cls : 'fa fa-chevron-right'
36432                                 }
36433                             ]
36434                         }
36435                     ]
36436                 }
36437             ]
36438         };
36439         
36440         return cfg;
36441     },
36442     
36443     initEvents : function()
36444     {
36445         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36446         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36447         
36448         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36449         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36450         
36451         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36452         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36453         
36454         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36455         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36456         
36457         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36458         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36459         
36460         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36461         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36462         
36463         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36464         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36465         
36466         this.thumbEl.on('click', this.onClick, this);
36467         
36468         this.prevIndicator.on('click', this.prev, this);
36469         
36470         this.nextIndicator.on('click', this.next, this);
36471         
36472     },
36473     
36474     initial : function()
36475     {
36476         if(this.files.length){
36477             this.indicator = 1;
36478             this.update()
36479         }
36480         
36481         this.fireEvent('initial', this);
36482     },
36483     
36484     update : function()
36485     {
36486         this.imageEl.attr('src', this.files[this.indicator - 1]);
36487         
36488         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36489         
36490         this.prevIndicator.show();
36491         
36492         if(this.indicator == 1){
36493             this.prevIndicator.hide();
36494         }
36495         
36496         this.nextIndicator.show();
36497         
36498         if(this.indicator == this.files.length){
36499             this.nextIndicator.hide();
36500         }
36501         
36502         this.thumbEl.scrollTo('top');
36503         
36504         this.fireEvent('update', this);
36505     },
36506     
36507     onClick : function(e)
36508     {
36509         e.preventDefault();
36510         
36511         this.fireEvent('click', this);
36512     },
36513     
36514     prev : function(e)
36515     {
36516         e.preventDefault();
36517         
36518         this.indicator = Math.max(1, this.indicator - 1);
36519         
36520         this.update();
36521     },
36522     
36523     next : function(e)
36524     {
36525         e.preventDefault();
36526         
36527         this.indicator = Math.min(this.files.length, this.indicator + 1);
36528         
36529         this.update();
36530     }
36531 });
36532 /*
36533  * - LGPL
36534  *
36535  * RadioSet
36536  *
36537  *
36538  */
36539
36540 /**
36541  * @class Roo.bootstrap.RadioSet
36542  * @extends Roo.bootstrap.Input
36543  * Bootstrap RadioSet class
36544  * @cfg {String} indicatorpos (left|right) default left
36545  * @cfg {Boolean} inline (true|false) inline the element (default true)
36546  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36547  * @constructor
36548  * Create a new RadioSet
36549  * @param {Object} config The config object
36550  */
36551
36552 Roo.bootstrap.RadioSet = function(config){
36553     
36554     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36555     
36556     this.radioes = [];
36557     
36558     Roo.bootstrap.RadioSet.register(this);
36559     
36560     this.addEvents({
36561         /**
36562         * @event check
36563         * Fires when the element is checked or unchecked.
36564         * @param {Roo.bootstrap.RadioSet} this This radio
36565         * @param {Roo.bootstrap.Radio} item The checked item
36566         */
36567        check : true,
36568        /**
36569         * @event click
36570         * Fires when the element is click.
36571         * @param {Roo.bootstrap.RadioSet} this This radio set
36572         * @param {Roo.bootstrap.Radio} item The checked item
36573         * @param {Roo.EventObject} e The event object
36574         */
36575        click : true
36576     });
36577     
36578 };
36579
36580 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36581
36582     radioes : false,
36583     
36584     inline : true,
36585     
36586     weight : '',
36587     
36588     indicatorpos : 'left',
36589     
36590     getAutoCreate : function()
36591     {
36592         var label = {
36593             tag : 'label',
36594             cls : 'roo-radio-set-label',
36595             cn : [
36596                 {
36597                     tag : 'span',
36598                     html : this.fieldLabel
36599                 }
36600             ]
36601         };
36602         if (Roo.bootstrap.version == 3) {
36603             
36604             
36605             if(this.indicatorpos == 'left'){
36606                 label.cn.unshift({
36607                     tag : 'i',
36608                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36609                     tooltip : 'This field is required'
36610                 });
36611             } else {
36612                 label.cn.push({
36613                     tag : 'i',
36614                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36615                     tooltip : 'This field is required'
36616                 });
36617             }
36618         }
36619         var items = {
36620             tag : 'div',
36621             cls : 'roo-radio-set-items'
36622         };
36623         
36624         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36625         
36626         if (align === 'left' && this.fieldLabel.length) {
36627             
36628             items = {
36629                 cls : "roo-radio-set-right", 
36630                 cn: [
36631                     items
36632                 ]
36633             };
36634             
36635             if(this.labelWidth > 12){
36636                 label.style = "width: " + this.labelWidth + 'px';
36637             }
36638             
36639             if(this.labelWidth < 13 && this.labelmd == 0){
36640                 this.labelmd = this.labelWidth;
36641             }
36642             
36643             if(this.labellg > 0){
36644                 label.cls += ' col-lg-' + this.labellg;
36645                 items.cls += ' col-lg-' + (12 - this.labellg);
36646             }
36647             
36648             if(this.labelmd > 0){
36649                 label.cls += ' col-md-' + this.labelmd;
36650                 items.cls += ' col-md-' + (12 - this.labelmd);
36651             }
36652             
36653             if(this.labelsm > 0){
36654                 label.cls += ' col-sm-' + this.labelsm;
36655                 items.cls += ' col-sm-' + (12 - this.labelsm);
36656             }
36657             
36658             if(this.labelxs > 0){
36659                 label.cls += ' col-xs-' + this.labelxs;
36660                 items.cls += ' col-xs-' + (12 - this.labelxs);
36661             }
36662         }
36663         
36664         var cfg = {
36665             tag : 'div',
36666             cls : 'roo-radio-set',
36667             cn : [
36668                 {
36669                     tag : 'input',
36670                     cls : 'roo-radio-set-input',
36671                     type : 'hidden',
36672                     name : this.name,
36673                     value : this.value ? this.value :  ''
36674                 },
36675                 label,
36676                 items
36677             ]
36678         };
36679         
36680         if(this.weight.length){
36681             cfg.cls += ' roo-radio-' + this.weight;
36682         }
36683         
36684         if(this.inline) {
36685             cfg.cls += ' roo-radio-set-inline';
36686         }
36687         
36688         var settings=this;
36689         ['xs','sm','md','lg'].map(function(size){
36690             if (settings[size]) {
36691                 cfg.cls += ' col-' + size + '-' + settings[size];
36692             }
36693         });
36694         
36695         return cfg;
36696         
36697     },
36698
36699     initEvents : function()
36700     {
36701         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36702         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36703         
36704         if(!this.fieldLabel.length){
36705             this.labelEl.hide();
36706         }
36707         
36708         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36709         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36710         
36711         this.indicator = this.indicatorEl();
36712         
36713         if(this.indicator){
36714             this.indicator.addClass('invisible');
36715         }
36716         
36717         this.originalValue = this.getValue();
36718         
36719     },
36720     
36721     inputEl: function ()
36722     {
36723         return this.el.select('.roo-radio-set-input', true).first();
36724     },
36725     
36726     getChildContainer : function()
36727     {
36728         return this.itemsEl;
36729     },
36730     
36731     register : function(item)
36732     {
36733         this.radioes.push(item);
36734         
36735     },
36736     
36737     validate : function()
36738     {   
36739         if(this.getVisibilityEl().hasClass('hidden')){
36740             return true;
36741         }
36742         
36743         var valid = false;
36744         
36745         Roo.each(this.radioes, function(i){
36746             if(!i.checked){
36747                 return;
36748             }
36749             
36750             valid = true;
36751             return false;
36752         });
36753         
36754         if(this.allowBlank) {
36755             return true;
36756         }
36757         
36758         if(this.disabled || valid){
36759             this.markValid();
36760             return true;
36761         }
36762         
36763         this.markInvalid();
36764         return false;
36765         
36766     },
36767     
36768     markValid : function()
36769     {
36770         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36771             this.indicatorEl().removeClass('visible');
36772             this.indicatorEl().addClass('invisible');
36773         }
36774         
36775         
36776         if (Roo.bootstrap.version == 3) {
36777             this.el.removeClass([this.invalidClass, this.validClass]);
36778             this.el.addClass(this.validClass);
36779         } else {
36780             this.el.removeClass(['is-invalid','is-valid']);
36781             this.el.addClass(['is-valid']);
36782         }
36783         this.fireEvent('valid', this);
36784     },
36785     
36786     markInvalid : function(msg)
36787     {
36788         if(this.allowBlank || this.disabled){
36789             return;
36790         }
36791         
36792         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36793             this.indicatorEl().removeClass('invisible');
36794             this.indicatorEl().addClass('visible');
36795         }
36796         if (Roo.bootstrap.version == 3) {
36797             this.el.removeClass([this.invalidClass, this.validClass]);
36798             this.el.addClass(this.invalidClass);
36799         } else {
36800             this.el.removeClass(['is-invalid','is-valid']);
36801             this.el.addClass(['is-invalid']);
36802         }
36803         
36804         this.fireEvent('invalid', this, msg);
36805         
36806     },
36807     
36808     setValue : function(v, suppressEvent)
36809     {   
36810         if(this.value === v){
36811             return;
36812         }
36813         
36814         this.value = v;
36815         
36816         if(this.rendered){
36817             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36818         }
36819         
36820         Roo.each(this.radioes, function(i){
36821             i.checked = false;
36822             i.el.removeClass('checked');
36823         });
36824         
36825         Roo.each(this.radioes, function(i){
36826             
36827             if(i.value === v || i.value.toString() === v.toString()){
36828                 i.checked = true;
36829                 i.el.addClass('checked');
36830                 
36831                 if(suppressEvent !== true){
36832                     this.fireEvent('check', this, i);
36833                 }
36834                 
36835                 return false;
36836             }
36837             
36838         }, this);
36839         
36840         this.validate();
36841     },
36842     
36843     clearInvalid : function(){
36844         
36845         if(!this.el || this.preventMark){
36846             return;
36847         }
36848         
36849         this.el.removeClass([this.invalidClass]);
36850         
36851         this.fireEvent('valid', this);
36852     }
36853     
36854 });
36855
36856 Roo.apply(Roo.bootstrap.RadioSet, {
36857     
36858     groups: {},
36859     
36860     register : function(set)
36861     {
36862         this.groups[set.name] = set;
36863     },
36864     
36865     get: function(name) 
36866     {
36867         if (typeof(this.groups[name]) == 'undefined') {
36868             return false;
36869         }
36870         
36871         return this.groups[name] ;
36872     }
36873     
36874 });
36875 /*
36876  * Based on:
36877  * Ext JS Library 1.1.1
36878  * Copyright(c) 2006-2007, Ext JS, LLC.
36879  *
36880  * Originally Released Under LGPL - original licence link has changed is not relivant.
36881  *
36882  * Fork - LGPL
36883  * <script type="text/javascript">
36884  */
36885
36886
36887 /**
36888  * @class Roo.bootstrap.SplitBar
36889  * @extends Roo.util.Observable
36890  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36891  * <br><br>
36892  * Usage:
36893  * <pre><code>
36894 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36895                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36896 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36897 split.minSize = 100;
36898 split.maxSize = 600;
36899 split.animate = true;
36900 split.on('moved', splitterMoved);
36901 </code></pre>
36902  * @constructor
36903  * Create a new SplitBar
36904  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36905  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36906  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36907  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36908                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36909                         position of the SplitBar).
36910  */
36911 Roo.bootstrap.SplitBar = function(cfg){
36912     
36913     /** @private */
36914     
36915     //{
36916     //  dragElement : elm
36917     //  resizingElement: el,
36918         // optional..
36919     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36920     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36921         // existingProxy ???
36922     //}
36923     
36924     this.el = Roo.get(cfg.dragElement, true);
36925     this.el.dom.unselectable = "on";
36926     /** @private */
36927     this.resizingEl = Roo.get(cfg.resizingElement, true);
36928
36929     /**
36930      * @private
36931      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36932      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36933      * @type Number
36934      */
36935     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36936     
36937     /**
36938      * The minimum size of the resizing element. (Defaults to 0)
36939      * @type Number
36940      */
36941     this.minSize = 0;
36942     
36943     /**
36944      * The maximum size of the resizing element. (Defaults to 2000)
36945      * @type Number
36946      */
36947     this.maxSize = 2000;
36948     
36949     /**
36950      * Whether to animate the transition to the new size
36951      * @type Boolean
36952      */
36953     this.animate = false;
36954     
36955     /**
36956      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36957      * @type Boolean
36958      */
36959     this.useShim = false;
36960     
36961     /** @private */
36962     this.shim = null;
36963     
36964     if(!cfg.existingProxy){
36965         /** @private */
36966         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36967     }else{
36968         this.proxy = Roo.get(cfg.existingProxy).dom;
36969     }
36970     /** @private */
36971     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36972     
36973     /** @private */
36974     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36975     
36976     /** @private */
36977     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36978     
36979     /** @private */
36980     this.dragSpecs = {};
36981     
36982     /**
36983      * @private The adapter to use to positon and resize elements
36984      */
36985     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36986     this.adapter.init(this);
36987     
36988     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36989         /** @private */
36990         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36991         this.el.addClass("roo-splitbar-h");
36992     }else{
36993         /** @private */
36994         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36995         this.el.addClass("roo-splitbar-v");
36996     }
36997     
36998     this.addEvents({
36999         /**
37000          * @event resize
37001          * Fires when the splitter is moved (alias for {@link #event-moved})
37002          * @param {Roo.bootstrap.SplitBar} this
37003          * @param {Number} newSize the new width or height
37004          */
37005         "resize" : true,
37006         /**
37007          * @event moved
37008          * Fires when the splitter is moved
37009          * @param {Roo.bootstrap.SplitBar} this
37010          * @param {Number} newSize the new width or height
37011          */
37012         "moved" : true,
37013         /**
37014          * @event beforeresize
37015          * Fires before the splitter is dragged
37016          * @param {Roo.bootstrap.SplitBar} this
37017          */
37018         "beforeresize" : true,
37019
37020         "beforeapply" : true
37021     });
37022
37023     Roo.util.Observable.call(this);
37024 };
37025
37026 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37027     onStartProxyDrag : function(x, y){
37028         this.fireEvent("beforeresize", this);
37029         if(!this.overlay){
37030             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37031             o.unselectable();
37032             o.enableDisplayMode("block");
37033             // all splitbars share the same overlay
37034             Roo.bootstrap.SplitBar.prototype.overlay = o;
37035         }
37036         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37037         this.overlay.show();
37038         Roo.get(this.proxy).setDisplayed("block");
37039         var size = this.adapter.getElementSize(this);
37040         this.activeMinSize = this.getMinimumSize();;
37041         this.activeMaxSize = this.getMaximumSize();;
37042         var c1 = size - this.activeMinSize;
37043         var c2 = Math.max(this.activeMaxSize - size, 0);
37044         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37045             this.dd.resetConstraints();
37046             this.dd.setXConstraint(
37047                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37048                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37049             );
37050             this.dd.setYConstraint(0, 0);
37051         }else{
37052             this.dd.resetConstraints();
37053             this.dd.setXConstraint(0, 0);
37054             this.dd.setYConstraint(
37055                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37056                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37057             );
37058          }
37059         this.dragSpecs.startSize = size;
37060         this.dragSpecs.startPoint = [x, y];
37061         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37062     },
37063     
37064     /** 
37065      * @private Called after the drag operation by the DDProxy
37066      */
37067     onEndProxyDrag : function(e){
37068         Roo.get(this.proxy).setDisplayed(false);
37069         var endPoint = Roo.lib.Event.getXY(e);
37070         if(this.overlay){
37071             this.overlay.hide();
37072         }
37073         var newSize;
37074         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37075             newSize = this.dragSpecs.startSize + 
37076                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37077                     endPoint[0] - this.dragSpecs.startPoint[0] :
37078                     this.dragSpecs.startPoint[0] - endPoint[0]
37079                 );
37080         }else{
37081             newSize = this.dragSpecs.startSize + 
37082                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37083                     endPoint[1] - this.dragSpecs.startPoint[1] :
37084                     this.dragSpecs.startPoint[1] - endPoint[1]
37085                 );
37086         }
37087         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37088         if(newSize != this.dragSpecs.startSize){
37089             if(this.fireEvent('beforeapply', this, newSize) !== false){
37090                 this.adapter.setElementSize(this, newSize);
37091                 this.fireEvent("moved", this, newSize);
37092                 this.fireEvent("resize", this, newSize);
37093             }
37094         }
37095     },
37096     
37097     /**
37098      * Get the adapter this SplitBar uses
37099      * @return The adapter object
37100      */
37101     getAdapter : function(){
37102         return this.adapter;
37103     },
37104     
37105     /**
37106      * Set the adapter this SplitBar uses
37107      * @param {Object} adapter A SplitBar adapter object
37108      */
37109     setAdapter : function(adapter){
37110         this.adapter = adapter;
37111         this.adapter.init(this);
37112     },
37113     
37114     /**
37115      * Gets the minimum size for the resizing element
37116      * @return {Number} The minimum size
37117      */
37118     getMinimumSize : function(){
37119         return this.minSize;
37120     },
37121     
37122     /**
37123      * Sets the minimum size for the resizing element
37124      * @param {Number} minSize The minimum size
37125      */
37126     setMinimumSize : function(minSize){
37127         this.minSize = minSize;
37128     },
37129     
37130     /**
37131      * Gets the maximum size for the resizing element
37132      * @return {Number} The maximum size
37133      */
37134     getMaximumSize : function(){
37135         return this.maxSize;
37136     },
37137     
37138     /**
37139      * Sets the maximum size for the resizing element
37140      * @param {Number} maxSize The maximum size
37141      */
37142     setMaximumSize : function(maxSize){
37143         this.maxSize = maxSize;
37144     },
37145     
37146     /**
37147      * Sets the initialize size for the resizing element
37148      * @param {Number} size The initial size
37149      */
37150     setCurrentSize : function(size){
37151         var oldAnimate = this.animate;
37152         this.animate = false;
37153         this.adapter.setElementSize(this, size);
37154         this.animate = oldAnimate;
37155     },
37156     
37157     /**
37158      * Destroy this splitbar. 
37159      * @param {Boolean} removeEl True to remove the element
37160      */
37161     destroy : function(removeEl){
37162         if(this.shim){
37163             this.shim.remove();
37164         }
37165         this.dd.unreg();
37166         this.proxy.parentNode.removeChild(this.proxy);
37167         if(removeEl){
37168             this.el.remove();
37169         }
37170     }
37171 });
37172
37173 /**
37174  * @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.
37175  */
37176 Roo.bootstrap.SplitBar.createProxy = function(dir){
37177     var proxy = new Roo.Element(document.createElement("div"));
37178     proxy.unselectable();
37179     var cls = 'roo-splitbar-proxy';
37180     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37181     document.body.appendChild(proxy.dom);
37182     return proxy.dom;
37183 };
37184
37185 /** 
37186  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37187  * Default Adapter. It assumes the splitter and resizing element are not positioned
37188  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37189  */
37190 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37191 };
37192
37193 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37194     // do nothing for now
37195     init : function(s){
37196     
37197     },
37198     /**
37199      * Called before drag operations to get the current size of the resizing element. 
37200      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37201      */
37202      getElementSize : function(s){
37203         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37204             return s.resizingEl.getWidth();
37205         }else{
37206             return s.resizingEl.getHeight();
37207         }
37208     },
37209     
37210     /**
37211      * Called after drag operations to set the size of the resizing element.
37212      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37213      * @param {Number} newSize The new size to set
37214      * @param {Function} onComplete A function to be invoked when resizing is complete
37215      */
37216     setElementSize : function(s, newSize, onComplete){
37217         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37218             if(!s.animate){
37219                 s.resizingEl.setWidth(newSize);
37220                 if(onComplete){
37221                     onComplete(s, newSize);
37222                 }
37223             }else{
37224                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37225             }
37226         }else{
37227             
37228             if(!s.animate){
37229                 s.resizingEl.setHeight(newSize);
37230                 if(onComplete){
37231                     onComplete(s, newSize);
37232                 }
37233             }else{
37234                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37235             }
37236         }
37237     }
37238 };
37239
37240 /** 
37241  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37242  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37243  * Adapter that  moves the splitter element to align with the resized sizing element. 
37244  * Used with an absolute positioned SplitBar.
37245  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37246  * document.body, make sure you assign an id to the body element.
37247  */
37248 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37249     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37250     this.container = Roo.get(container);
37251 };
37252
37253 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37254     init : function(s){
37255         this.basic.init(s);
37256     },
37257     
37258     getElementSize : function(s){
37259         return this.basic.getElementSize(s);
37260     },
37261     
37262     setElementSize : function(s, newSize, onComplete){
37263         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37264     },
37265     
37266     moveSplitter : function(s){
37267         var yes = Roo.bootstrap.SplitBar;
37268         switch(s.placement){
37269             case yes.LEFT:
37270                 s.el.setX(s.resizingEl.getRight());
37271                 break;
37272             case yes.RIGHT:
37273                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37274                 break;
37275             case yes.TOP:
37276                 s.el.setY(s.resizingEl.getBottom());
37277                 break;
37278             case yes.BOTTOM:
37279                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37280                 break;
37281         }
37282     }
37283 };
37284
37285 /**
37286  * Orientation constant - Create a vertical SplitBar
37287  * @static
37288  * @type Number
37289  */
37290 Roo.bootstrap.SplitBar.VERTICAL = 1;
37291
37292 /**
37293  * Orientation constant - Create a horizontal SplitBar
37294  * @static
37295  * @type Number
37296  */
37297 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37298
37299 /**
37300  * Placement constant - The resizing element is to the left of the splitter element
37301  * @static
37302  * @type Number
37303  */
37304 Roo.bootstrap.SplitBar.LEFT = 1;
37305
37306 /**
37307  * Placement constant - The resizing element is to the right of the splitter element
37308  * @static
37309  * @type Number
37310  */
37311 Roo.bootstrap.SplitBar.RIGHT = 2;
37312
37313 /**
37314  * Placement constant - The resizing element is positioned above the splitter element
37315  * @static
37316  * @type Number
37317  */
37318 Roo.bootstrap.SplitBar.TOP = 3;
37319
37320 /**
37321  * Placement constant - The resizing element is positioned under splitter element
37322  * @static
37323  * @type Number
37324  */
37325 Roo.bootstrap.SplitBar.BOTTOM = 4;
37326 Roo.namespace("Roo.bootstrap.layout");/*
37327  * Based on:
37328  * Ext JS Library 1.1.1
37329  * Copyright(c) 2006-2007, Ext JS, LLC.
37330  *
37331  * Originally Released Under LGPL - original licence link has changed is not relivant.
37332  *
37333  * Fork - LGPL
37334  * <script type="text/javascript">
37335  */
37336
37337 /**
37338  * @class Roo.bootstrap.layout.Manager
37339  * @extends Roo.bootstrap.Component
37340  * Base class for layout managers.
37341  */
37342 Roo.bootstrap.layout.Manager = function(config)
37343 {
37344     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37345
37346
37347
37348
37349
37350     /** false to disable window resize monitoring @type Boolean */
37351     this.monitorWindowResize = true;
37352     this.regions = {};
37353     this.addEvents({
37354         /**
37355          * @event layout
37356          * Fires when a layout is performed.
37357          * @param {Roo.LayoutManager} this
37358          */
37359         "layout" : true,
37360         /**
37361          * @event regionresized
37362          * Fires when the user resizes a region.
37363          * @param {Roo.LayoutRegion} region The resized region
37364          * @param {Number} newSize The new size (width for east/west, height for north/south)
37365          */
37366         "regionresized" : true,
37367         /**
37368          * @event regioncollapsed
37369          * Fires when a region is collapsed.
37370          * @param {Roo.LayoutRegion} region The collapsed region
37371          */
37372         "regioncollapsed" : true,
37373         /**
37374          * @event regionexpanded
37375          * Fires when a region is expanded.
37376          * @param {Roo.LayoutRegion} region The expanded region
37377          */
37378         "regionexpanded" : true
37379     });
37380     this.updating = false;
37381
37382     if (config.el) {
37383         this.el = Roo.get(config.el);
37384         this.initEvents();
37385     }
37386
37387 };
37388
37389 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37390
37391
37392     regions : null,
37393
37394     monitorWindowResize : true,
37395
37396
37397     updating : false,
37398
37399
37400     onRender : function(ct, position)
37401     {
37402         if(!this.el){
37403             this.el = Roo.get(ct);
37404             this.initEvents();
37405         }
37406         //this.fireEvent('render',this);
37407     },
37408
37409
37410     initEvents: function()
37411     {
37412
37413
37414         // ie scrollbar fix
37415         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37416             document.body.scroll = "no";
37417         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37418             this.el.position('relative');
37419         }
37420         this.id = this.el.id;
37421         this.el.addClass("roo-layout-container");
37422         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37423         if(this.el.dom != document.body ) {
37424             this.el.on('resize', this.layout,this);
37425             this.el.on('show', this.layout,this);
37426         }
37427
37428     },
37429
37430     /**
37431      * Returns true if this layout is currently being updated
37432      * @return {Boolean}
37433      */
37434     isUpdating : function(){
37435         return this.updating;
37436     },
37437
37438     /**
37439      * Suspend the LayoutManager from doing auto-layouts while
37440      * making multiple add or remove calls
37441      */
37442     beginUpdate : function(){
37443         this.updating = true;
37444     },
37445
37446     /**
37447      * Restore auto-layouts and optionally disable the manager from performing a layout
37448      * @param {Boolean} noLayout true to disable a layout update
37449      */
37450     endUpdate : function(noLayout){
37451         this.updating = false;
37452         if(!noLayout){
37453             this.layout();
37454         }
37455     },
37456
37457     layout: function(){
37458         // abstract...
37459     },
37460
37461     onRegionResized : function(region, newSize){
37462         this.fireEvent("regionresized", region, newSize);
37463         this.layout();
37464     },
37465
37466     onRegionCollapsed : function(region){
37467         this.fireEvent("regioncollapsed", region);
37468     },
37469
37470     onRegionExpanded : function(region){
37471         this.fireEvent("regionexpanded", region);
37472     },
37473
37474     /**
37475      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37476      * performs box-model adjustments.
37477      * @return {Object} The size as an object {width: (the width), height: (the height)}
37478      */
37479     getViewSize : function()
37480     {
37481         var size;
37482         if(this.el.dom != document.body){
37483             size = this.el.getSize();
37484         }else{
37485             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37486         }
37487         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37488         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37489         return size;
37490     },
37491
37492     /**
37493      * Returns the Element this layout is bound to.
37494      * @return {Roo.Element}
37495      */
37496     getEl : function(){
37497         return this.el;
37498     },
37499
37500     /**
37501      * Returns the specified region.
37502      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37503      * @return {Roo.LayoutRegion}
37504      */
37505     getRegion : function(target){
37506         return this.regions[target.toLowerCase()];
37507     },
37508
37509     onWindowResize : function(){
37510         if(this.monitorWindowResize){
37511             this.layout();
37512         }
37513     }
37514 });
37515 /*
37516  * Based on:
37517  * Ext JS Library 1.1.1
37518  * Copyright(c) 2006-2007, Ext JS, LLC.
37519  *
37520  * Originally Released Under LGPL - original licence link has changed is not relivant.
37521  *
37522  * Fork - LGPL
37523  * <script type="text/javascript">
37524  */
37525 /**
37526  * @class Roo.bootstrap.layout.Border
37527  * @extends Roo.bootstrap.layout.Manager
37528  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37529  * please see: examples/bootstrap/nested.html<br><br>
37530  
37531 <b>The container the layout is rendered into can be either the body element or any other element.
37532 If it is not the body element, the container needs to either be an absolute positioned element,
37533 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37534 the container size if it is not the body element.</b>
37535
37536 * @constructor
37537 * Create a new Border
37538 * @param {Object} config Configuration options
37539  */
37540 Roo.bootstrap.layout.Border = function(config){
37541     config = config || {};
37542     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37543     
37544     
37545     
37546     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37547         if(config[region]){
37548             config[region].region = region;
37549             this.addRegion(config[region]);
37550         }
37551     },this);
37552     
37553 };
37554
37555 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37556
37557 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37558     
37559     parent : false, // this might point to a 'nest' or a ???
37560     
37561     /**
37562      * Creates and adds a new region if it doesn't already exist.
37563      * @param {String} target The target region key (north, south, east, west or center).
37564      * @param {Object} config The regions config object
37565      * @return {BorderLayoutRegion} The new region
37566      */
37567     addRegion : function(config)
37568     {
37569         if(!this.regions[config.region]){
37570             var r = this.factory(config);
37571             this.bindRegion(r);
37572         }
37573         return this.regions[config.region];
37574     },
37575
37576     // private (kinda)
37577     bindRegion : function(r){
37578         this.regions[r.config.region] = r;
37579         
37580         r.on("visibilitychange",    this.layout, this);
37581         r.on("paneladded",          this.layout, this);
37582         r.on("panelremoved",        this.layout, this);
37583         r.on("invalidated",         this.layout, this);
37584         r.on("resized",             this.onRegionResized, this);
37585         r.on("collapsed",           this.onRegionCollapsed, this);
37586         r.on("expanded",            this.onRegionExpanded, this);
37587     },
37588
37589     /**
37590      * Performs a layout update.
37591      */
37592     layout : function()
37593     {
37594         if(this.updating) {
37595             return;
37596         }
37597         
37598         // render all the rebions if they have not been done alreayd?
37599         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37600             if(this.regions[region] && !this.regions[region].bodyEl){
37601                 this.regions[region].onRender(this.el)
37602             }
37603         },this);
37604         
37605         var size = this.getViewSize();
37606         var w = size.width;
37607         var h = size.height;
37608         var centerW = w;
37609         var centerH = h;
37610         var centerY = 0;
37611         var centerX = 0;
37612         //var x = 0, y = 0;
37613
37614         var rs = this.regions;
37615         var north = rs["north"];
37616         var south = rs["south"]; 
37617         var west = rs["west"];
37618         var east = rs["east"];
37619         var center = rs["center"];
37620         //if(this.hideOnLayout){ // not supported anymore
37621             //c.el.setStyle("display", "none");
37622         //}
37623         if(north && north.isVisible()){
37624             var b = north.getBox();
37625             var m = north.getMargins();
37626             b.width = w - (m.left+m.right);
37627             b.x = m.left;
37628             b.y = m.top;
37629             centerY = b.height + b.y + m.bottom;
37630             centerH -= centerY;
37631             north.updateBox(this.safeBox(b));
37632         }
37633         if(south && south.isVisible()){
37634             var b = south.getBox();
37635             var m = south.getMargins();
37636             b.width = w - (m.left+m.right);
37637             b.x = m.left;
37638             var totalHeight = (b.height + m.top + m.bottom);
37639             b.y = h - totalHeight + m.top;
37640             centerH -= totalHeight;
37641             south.updateBox(this.safeBox(b));
37642         }
37643         if(west && west.isVisible()){
37644             var b = west.getBox();
37645             var m = west.getMargins();
37646             b.height = centerH - (m.top+m.bottom);
37647             b.x = m.left;
37648             b.y = centerY + m.top;
37649             var totalWidth = (b.width + m.left + m.right);
37650             centerX += totalWidth;
37651             centerW -= totalWidth;
37652             west.updateBox(this.safeBox(b));
37653         }
37654         if(east && east.isVisible()){
37655             var b = east.getBox();
37656             var m = east.getMargins();
37657             b.height = centerH - (m.top+m.bottom);
37658             var totalWidth = (b.width + m.left + m.right);
37659             b.x = w - totalWidth + m.left;
37660             b.y = centerY + m.top;
37661             centerW -= totalWidth;
37662             east.updateBox(this.safeBox(b));
37663         }
37664         if(center){
37665             var m = center.getMargins();
37666             var centerBox = {
37667                 x: centerX + m.left,
37668                 y: centerY + m.top,
37669                 width: centerW - (m.left+m.right),
37670                 height: centerH - (m.top+m.bottom)
37671             };
37672             //if(this.hideOnLayout){
37673                 //center.el.setStyle("display", "block");
37674             //}
37675             center.updateBox(this.safeBox(centerBox));
37676         }
37677         this.el.repaint();
37678         this.fireEvent("layout", this);
37679     },
37680
37681     // private
37682     safeBox : function(box){
37683         box.width = Math.max(0, box.width);
37684         box.height = Math.max(0, box.height);
37685         return box;
37686     },
37687
37688     /**
37689      * Adds a ContentPanel (or subclass) to this layout.
37690      * @param {String} target The target region key (north, south, east, west or center).
37691      * @param {Roo.ContentPanel} panel The panel to add
37692      * @return {Roo.ContentPanel} The added panel
37693      */
37694     add : function(target, panel){
37695          
37696         target = target.toLowerCase();
37697         return this.regions[target].add(panel);
37698     },
37699
37700     /**
37701      * Remove a ContentPanel (or subclass) to this layout.
37702      * @param {String} target The target region key (north, south, east, west or center).
37703      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37704      * @return {Roo.ContentPanel} The removed panel
37705      */
37706     remove : function(target, panel){
37707         target = target.toLowerCase();
37708         return this.regions[target].remove(panel);
37709     },
37710
37711     /**
37712      * Searches all regions for a panel with the specified id
37713      * @param {String} panelId
37714      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37715      */
37716     findPanel : function(panelId){
37717         var rs = this.regions;
37718         for(var target in rs){
37719             if(typeof rs[target] != "function"){
37720                 var p = rs[target].getPanel(panelId);
37721                 if(p){
37722                     return p;
37723                 }
37724             }
37725         }
37726         return null;
37727     },
37728
37729     /**
37730      * Searches all regions for a panel with the specified id and activates (shows) it.
37731      * @param {String/ContentPanel} panelId The panels id or the panel itself
37732      * @return {Roo.ContentPanel} The shown panel or null
37733      */
37734     showPanel : function(panelId) {
37735       var rs = this.regions;
37736       for(var target in rs){
37737          var r = rs[target];
37738          if(typeof r != "function"){
37739             if(r.hasPanel(panelId)){
37740                return r.showPanel(panelId);
37741             }
37742          }
37743       }
37744       return null;
37745    },
37746
37747    /**
37748      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37749      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37750      */
37751    /*
37752     restoreState : function(provider){
37753         if(!provider){
37754             provider = Roo.state.Manager;
37755         }
37756         var sm = new Roo.LayoutStateManager();
37757         sm.init(this, provider);
37758     },
37759 */
37760  
37761  
37762     /**
37763      * Adds a xtype elements to the layout.
37764      * <pre><code>
37765
37766 layout.addxtype({
37767        xtype : 'ContentPanel',
37768        region: 'west',
37769        items: [ .... ]
37770    }
37771 );
37772
37773 layout.addxtype({
37774         xtype : 'NestedLayoutPanel',
37775         region: 'west',
37776         layout: {
37777            center: { },
37778            west: { }   
37779         },
37780         items : [ ... list of content panels or nested layout panels.. ]
37781    }
37782 );
37783 </code></pre>
37784      * @param {Object} cfg Xtype definition of item to add.
37785      */
37786     addxtype : function(cfg)
37787     {
37788         // basically accepts a pannel...
37789         // can accept a layout region..!?!?
37790         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37791         
37792         
37793         // theory?  children can only be panels??
37794         
37795         //if (!cfg.xtype.match(/Panel$/)) {
37796         //    return false;
37797         //}
37798         var ret = false;
37799         
37800         if (typeof(cfg.region) == 'undefined') {
37801             Roo.log("Failed to add Panel, region was not set");
37802             Roo.log(cfg);
37803             return false;
37804         }
37805         var region = cfg.region;
37806         delete cfg.region;
37807         
37808           
37809         var xitems = [];
37810         if (cfg.items) {
37811             xitems = cfg.items;
37812             delete cfg.items;
37813         }
37814         var nb = false;
37815         
37816         if ( region == 'center') {
37817             Roo.log("Center: " + cfg.title);
37818         }
37819         
37820         
37821         switch(cfg.xtype) 
37822         {
37823             case 'Content':  // ContentPanel (el, cfg)
37824             case 'Scroll':  // ContentPanel (el, cfg)
37825             case 'View': 
37826                 cfg.autoCreate = cfg.autoCreate || true;
37827                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37828                 //} else {
37829                 //    var el = this.el.createChild();
37830                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37831                 //}
37832                 
37833                 this.add(region, ret);
37834                 break;
37835             
37836             /*
37837             case 'TreePanel': // our new panel!
37838                 cfg.el = this.el.createChild();
37839                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37840                 this.add(region, ret);
37841                 break;
37842             */
37843             
37844             case 'Nest': 
37845                 // create a new Layout (which is  a Border Layout...
37846                 
37847                 var clayout = cfg.layout;
37848                 clayout.el  = this.el.createChild();
37849                 clayout.items   = clayout.items  || [];
37850                 
37851                 delete cfg.layout;
37852                 
37853                 // replace this exitems with the clayout ones..
37854                 xitems = clayout.items;
37855                  
37856                 // force background off if it's in center...
37857                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37858                     cfg.background = false;
37859                 }
37860                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37861                 
37862                 
37863                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37864                 //console.log('adding nested layout panel '  + cfg.toSource());
37865                 this.add(region, ret);
37866                 nb = {}; /// find first...
37867                 break;
37868             
37869             case 'Grid':
37870                 
37871                 // needs grid and region
37872                 
37873                 //var el = this.getRegion(region).el.createChild();
37874                 /*
37875                  *var el = this.el.createChild();
37876                 // create the grid first...
37877                 cfg.grid.container = el;
37878                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37879                 */
37880                 
37881                 if (region == 'center' && this.active ) {
37882                     cfg.background = false;
37883                 }
37884                 
37885                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37886                 
37887                 this.add(region, ret);
37888                 /*
37889                 if (cfg.background) {
37890                     // render grid on panel activation (if panel background)
37891                     ret.on('activate', function(gp) {
37892                         if (!gp.grid.rendered) {
37893                     //        gp.grid.render(el);
37894                         }
37895                     });
37896                 } else {
37897                   //  cfg.grid.render(el);
37898                 }
37899                 */
37900                 break;
37901            
37902            
37903             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37904                 // it was the old xcomponent building that caused this before.
37905                 // espeically if border is the top element in the tree.
37906                 ret = this;
37907                 break; 
37908                 
37909                     
37910                 
37911                 
37912                 
37913             default:
37914                 /*
37915                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37916                     
37917                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37918                     this.add(region, ret);
37919                 } else {
37920                 */
37921                     Roo.log(cfg);
37922                     throw "Can not add '" + cfg.xtype + "' to Border";
37923                     return null;
37924              
37925                                 
37926              
37927         }
37928         this.beginUpdate();
37929         // add children..
37930         var region = '';
37931         var abn = {};
37932         Roo.each(xitems, function(i)  {
37933             region = nb && i.region ? i.region : false;
37934             
37935             var add = ret.addxtype(i);
37936            
37937             if (region) {
37938                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37939                 if (!i.background) {
37940                     abn[region] = nb[region] ;
37941                 }
37942             }
37943             
37944         });
37945         this.endUpdate();
37946
37947         // make the last non-background panel active..
37948         //if (nb) { Roo.log(abn); }
37949         if (nb) {
37950             
37951             for(var r in abn) {
37952                 region = this.getRegion(r);
37953                 if (region) {
37954                     // tried using nb[r], but it does not work..
37955                      
37956                     region.showPanel(abn[r]);
37957                    
37958                 }
37959             }
37960         }
37961         return ret;
37962         
37963     },
37964     
37965     
37966 // private
37967     factory : function(cfg)
37968     {
37969         
37970         var validRegions = Roo.bootstrap.layout.Border.regions;
37971
37972         var target = cfg.region;
37973         cfg.mgr = this;
37974         
37975         var r = Roo.bootstrap.layout;
37976         Roo.log(target);
37977         switch(target){
37978             case "north":
37979                 return new r.North(cfg);
37980             case "south":
37981                 return new r.South(cfg);
37982             case "east":
37983                 return new r.East(cfg);
37984             case "west":
37985                 return new r.West(cfg);
37986             case "center":
37987                 return new r.Center(cfg);
37988         }
37989         throw 'Layout region "'+target+'" not supported.';
37990     }
37991     
37992     
37993 });
37994  /*
37995  * Based on:
37996  * Ext JS Library 1.1.1
37997  * Copyright(c) 2006-2007, Ext JS, LLC.
37998  *
37999  * Originally Released Under LGPL - original licence link has changed is not relivant.
38000  *
38001  * Fork - LGPL
38002  * <script type="text/javascript">
38003  */
38004  
38005 /**
38006  * @class Roo.bootstrap.layout.Basic
38007  * @extends Roo.util.Observable
38008  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38009  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38010  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38011  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38012  * @cfg {string}   region  the region that it inhabits..
38013  * @cfg {bool}   skipConfig skip config?
38014  * 
38015
38016  */
38017 Roo.bootstrap.layout.Basic = function(config){
38018     
38019     this.mgr = config.mgr;
38020     
38021     this.position = config.region;
38022     
38023     var skipConfig = config.skipConfig;
38024     
38025     this.events = {
38026         /**
38027          * @scope Roo.BasicLayoutRegion
38028          */
38029         
38030         /**
38031          * @event beforeremove
38032          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38033          * @param {Roo.LayoutRegion} this
38034          * @param {Roo.ContentPanel} panel The panel
38035          * @param {Object} e The cancel event object
38036          */
38037         "beforeremove" : true,
38038         /**
38039          * @event invalidated
38040          * Fires when the layout for this region is changed.
38041          * @param {Roo.LayoutRegion} this
38042          */
38043         "invalidated" : true,
38044         /**
38045          * @event visibilitychange
38046          * Fires when this region is shown or hidden 
38047          * @param {Roo.LayoutRegion} this
38048          * @param {Boolean} visibility true or false
38049          */
38050         "visibilitychange" : true,
38051         /**
38052          * @event paneladded
38053          * Fires when a panel is added. 
38054          * @param {Roo.LayoutRegion} this
38055          * @param {Roo.ContentPanel} panel The panel
38056          */
38057         "paneladded" : true,
38058         /**
38059          * @event panelremoved
38060          * Fires when a panel is removed. 
38061          * @param {Roo.LayoutRegion} this
38062          * @param {Roo.ContentPanel} panel The panel
38063          */
38064         "panelremoved" : true,
38065         /**
38066          * @event beforecollapse
38067          * Fires when this region before collapse.
38068          * @param {Roo.LayoutRegion} this
38069          */
38070         "beforecollapse" : true,
38071         /**
38072          * @event collapsed
38073          * Fires when this region is collapsed.
38074          * @param {Roo.LayoutRegion} this
38075          */
38076         "collapsed" : true,
38077         /**
38078          * @event expanded
38079          * Fires when this region is expanded.
38080          * @param {Roo.LayoutRegion} this
38081          */
38082         "expanded" : true,
38083         /**
38084          * @event slideshow
38085          * Fires when this region is slid into view.
38086          * @param {Roo.LayoutRegion} this
38087          */
38088         "slideshow" : true,
38089         /**
38090          * @event slidehide
38091          * Fires when this region slides out of view. 
38092          * @param {Roo.LayoutRegion} this
38093          */
38094         "slidehide" : true,
38095         /**
38096          * @event panelactivated
38097          * Fires when a panel is activated. 
38098          * @param {Roo.LayoutRegion} this
38099          * @param {Roo.ContentPanel} panel The activated panel
38100          */
38101         "panelactivated" : true,
38102         /**
38103          * @event resized
38104          * Fires when the user resizes this region. 
38105          * @param {Roo.LayoutRegion} this
38106          * @param {Number} newSize The new size (width for east/west, height for north/south)
38107          */
38108         "resized" : true
38109     };
38110     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38111     this.panels = new Roo.util.MixedCollection();
38112     this.panels.getKey = this.getPanelId.createDelegate(this);
38113     this.box = null;
38114     this.activePanel = null;
38115     // ensure listeners are added...
38116     
38117     if (config.listeners || config.events) {
38118         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38119             listeners : config.listeners || {},
38120             events : config.events || {}
38121         });
38122     }
38123     
38124     if(skipConfig !== true){
38125         this.applyConfig(config);
38126     }
38127 };
38128
38129 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38130 {
38131     getPanelId : function(p){
38132         return p.getId();
38133     },
38134     
38135     applyConfig : function(config){
38136         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38137         this.config = config;
38138         
38139     },
38140     
38141     /**
38142      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38143      * the width, for horizontal (north, south) the height.
38144      * @param {Number} newSize The new width or height
38145      */
38146     resizeTo : function(newSize){
38147         var el = this.el ? this.el :
38148                  (this.activePanel ? this.activePanel.getEl() : null);
38149         if(el){
38150             switch(this.position){
38151                 case "east":
38152                 case "west":
38153                     el.setWidth(newSize);
38154                     this.fireEvent("resized", this, newSize);
38155                 break;
38156                 case "north":
38157                 case "south":
38158                     el.setHeight(newSize);
38159                     this.fireEvent("resized", this, newSize);
38160                 break;                
38161             }
38162         }
38163     },
38164     
38165     getBox : function(){
38166         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38167     },
38168     
38169     getMargins : function(){
38170         return this.margins;
38171     },
38172     
38173     updateBox : function(box){
38174         this.box = box;
38175         var el = this.activePanel.getEl();
38176         el.dom.style.left = box.x + "px";
38177         el.dom.style.top = box.y + "px";
38178         this.activePanel.setSize(box.width, box.height);
38179     },
38180     
38181     /**
38182      * Returns the container element for this region.
38183      * @return {Roo.Element}
38184      */
38185     getEl : function(){
38186         return this.activePanel;
38187     },
38188     
38189     /**
38190      * Returns true if this region is currently visible.
38191      * @return {Boolean}
38192      */
38193     isVisible : function(){
38194         return this.activePanel ? true : false;
38195     },
38196     
38197     setActivePanel : function(panel){
38198         panel = this.getPanel(panel);
38199         if(this.activePanel && this.activePanel != panel){
38200             this.activePanel.setActiveState(false);
38201             this.activePanel.getEl().setLeftTop(-10000,-10000);
38202         }
38203         this.activePanel = panel;
38204         panel.setActiveState(true);
38205         if(this.box){
38206             panel.setSize(this.box.width, this.box.height);
38207         }
38208         this.fireEvent("panelactivated", this, panel);
38209         this.fireEvent("invalidated");
38210     },
38211     
38212     /**
38213      * Show the specified panel.
38214      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38215      * @return {Roo.ContentPanel} The shown panel or null
38216      */
38217     showPanel : function(panel){
38218         panel = this.getPanel(panel);
38219         if(panel){
38220             this.setActivePanel(panel);
38221         }
38222         return panel;
38223     },
38224     
38225     /**
38226      * Get the active panel for this region.
38227      * @return {Roo.ContentPanel} The active panel or null
38228      */
38229     getActivePanel : function(){
38230         return this.activePanel;
38231     },
38232     
38233     /**
38234      * Add the passed ContentPanel(s)
38235      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38236      * @return {Roo.ContentPanel} The panel added (if only one was added)
38237      */
38238     add : function(panel){
38239         if(arguments.length > 1){
38240             for(var i = 0, len = arguments.length; i < len; i++) {
38241                 this.add(arguments[i]);
38242             }
38243             return null;
38244         }
38245         if(this.hasPanel(panel)){
38246             this.showPanel(panel);
38247             return panel;
38248         }
38249         var el = panel.getEl();
38250         if(el.dom.parentNode != this.mgr.el.dom){
38251             this.mgr.el.dom.appendChild(el.dom);
38252         }
38253         if(panel.setRegion){
38254             panel.setRegion(this);
38255         }
38256         this.panels.add(panel);
38257         el.setStyle("position", "absolute");
38258         if(!panel.background){
38259             this.setActivePanel(panel);
38260             if(this.config.initialSize && this.panels.getCount()==1){
38261                 this.resizeTo(this.config.initialSize);
38262             }
38263         }
38264         this.fireEvent("paneladded", this, panel);
38265         return panel;
38266     },
38267     
38268     /**
38269      * Returns true if the panel is in this region.
38270      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38271      * @return {Boolean}
38272      */
38273     hasPanel : function(panel){
38274         if(typeof panel == "object"){ // must be panel obj
38275             panel = panel.getId();
38276         }
38277         return this.getPanel(panel) ? true : false;
38278     },
38279     
38280     /**
38281      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38282      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38283      * @param {Boolean} preservePanel Overrides the config preservePanel option
38284      * @return {Roo.ContentPanel} The panel that was removed
38285      */
38286     remove : function(panel, preservePanel){
38287         panel = this.getPanel(panel);
38288         if(!panel){
38289             return null;
38290         }
38291         var e = {};
38292         this.fireEvent("beforeremove", this, panel, e);
38293         if(e.cancel === true){
38294             return null;
38295         }
38296         var panelId = panel.getId();
38297         this.panels.removeKey(panelId);
38298         return panel;
38299     },
38300     
38301     /**
38302      * Returns the panel specified or null if it's not in this region.
38303      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38304      * @return {Roo.ContentPanel}
38305      */
38306     getPanel : function(id){
38307         if(typeof id == "object"){ // must be panel obj
38308             return id;
38309         }
38310         return this.panels.get(id);
38311     },
38312     
38313     /**
38314      * Returns this regions position (north/south/east/west/center).
38315      * @return {String} 
38316      */
38317     getPosition: function(){
38318         return this.position;    
38319     }
38320 });/*
38321  * Based on:
38322  * Ext JS Library 1.1.1
38323  * Copyright(c) 2006-2007, Ext JS, LLC.
38324  *
38325  * Originally Released Under LGPL - original licence link has changed is not relivant.
38326  *
38327  * Fork - LGPL
38328  * <script type="text/javascript">
38329  */
38330  
38331 /**
38332  * @class Roo.bootstrap.layout.Region
38333  * @extends Roo.bootstrap.layout.Basic
38334  * This class represents a region in a layout manager.
38335  
38336  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38337  * @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})
38338  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38339  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38340  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38341  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38342  * @cfg {String}    title           The title for the region (overrides panel titles)
38343  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38344  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38345  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38346  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38347  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38348  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38349  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38350  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38351  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38352  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38353
38354  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38355  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38356  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38357  * @cfg {Number}    width           For East/West panels
38358  * @cfg {Number}    height          For North/South panels
38359  * @cfg {Boolean}   split           To show the splitter
38360  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38361  * 
38362  * @cfg {string}   cls             Extra CSS classes to add to region
38363  * 
38364  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38365  * @cfg {string}   region  the region that it inhabits..
38366  *
38367
38368  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38369  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38370
38371  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38372  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38373  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38374  */
38375 Roo.bootstrap.layout.Region = function(config)
38376 {
38377     this.applyConfig(config);
38378
38379     var mgr = config.mgr;
38380     var pos = config.region;
38381     config.skipConfig = true;
38382     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38383     
38384     if (mgr.el) {
38385         this.onRender(mgr.el);   
38386     }
38387      
38388     this.visible = true;
38389     this.collapsed = false;
38390     this.unrendered_panels = [];
38391 };
38392
38393 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38394
38395     position: '', // set by wrapper (eg. north/south etc..)
38396     unrendered_panels : null,  // unrendered panels.
38397     
38398     tabPosition : false,
38399     
38400     mgr: false, // points to 'Border'
38401     
38402     
38403     createBody : function(){
38404         /** This region's body element 
38405         * @type Roo.Element */
38406         this.bodyEl = this.el.createChild({
38407                 tag: "div",
38408                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38409         });
38410     },
38411
38412     onRender: function(ctr, pos)
38413     {
38414         var dh = Roo.DomHelper;
38415         /** This region's container element 
38416         * @type Roo.Element */
38417         this.el = dh.append(ctr.dom, {
38418                 tag: "div",
38419                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38420             }, true);
38421         /** This region's title element 
38422         * @type Roo.Element */
38423     
38424         this.titleEl = dh.append(this.el.dom,  {
38425                 tag: "div",
38426                 unselectable: "on",
38427                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38428                 children:[
38429                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38430                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38431                 ]
38432             }, true);
38433         
38434         this.titleEl.enableDisplayMode();
38435         /** This region's title text element 
38436         * @type HTMLElement */
38437         this.titleTextEl = this.titleEl.dom.firstChild;
38438         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38439         /*
38440         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38441         this.closeBtn.enableDisplayMode();
38442         this.closeBtn.on("click", this.closeClicked, this);
38443         this.closeBtn.hide();
38444     */
38445         this.createBody(this.config);
38446         if(this.config.hideWhenEmpty){
38447             this.hide();
38448             this.on("paneladded", this.validateVisibility, this);
38449             this.on("panelremoved", this.validateVisibility, this);
38450         }
38451         if(this.autoScroll){
38452             this.bodyEl.setStyle("overflow", "auto");
38453         }else{
38454             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38455         }
38456         //if(c.titlebar !== false){
38457             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38458                 this.titleEl.hide();
38459             }else{
38460                 this.titleEl.show();
38461                 if(this.config.title){
38462                     this.titleTextEl.innerHTML = this.config.title;
38463                 }
38464             }
38465         //}
38466         if(this.config.collapsed){
38467             this.collapse(true);
38468         }
38469         if(this.config.hidden){
38470             this.hide();
38471         }
38472         
38473         if (this.unrendered_panels && this.unrendered_panels.length) {
38474             for (var i =0;i< this.unrendered_panels.length; i++) {
38475                 this.add(this.unrendered_panels[i]);
38476             }
38477             this.unrendered_panels = null;
38478             
38479         }
38480         
38481     },
38482     
38483     applyConfig : function(c)
38484     {
38485         /*
38486          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38487             var dh = Roo.DomHelper;
38488             if(c.titlebar !== false){
38489                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38490                 this.collapseBtn.on("click", this.collapse, this);
38491                 this.collapseBtn.enableDisplayMode();
38492                 /*
38493                 if(c.showPin === true || this.showPin){
38494                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38495                     this.stickBtn.enableDisplayMode();
38496                     this.stickBtn.on("click", this.expand, this);
38497                     this.stickBtn.hide();
38498                 }
38499                 
38500             }
38501             */
38502             /** This region's collapsed element
38503             * @type Roo.Element */
38504             /*
38505              *
38506             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38507                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38508             ]}, true);
38509             
38510             if(c.floatable !== false){
38511                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38512                this.collapsedEl.on("click", this.collapseClick, this);
38513             }
38514
38515             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38516                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38517                    id: "message", unselectable: "on", style:{"float":"left"}});
38518                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38519              }
38520             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38521             this.expandBtn.on("click", this.expand, this);
38522             
38523         }
38524         
38525         if(this.collapseBtn){
38526             this.collapseBtn.setVisible(c.collapsible == true);
38527         }
38528         
38529         this.cmargins = c.cmargins || this.cmargins ||
38530                          (this.position == "west" || this.position == "east" ?
38531                              {top: 0, left: 2, right:2, bottom: 0} :
38532                              {top: 2, left: 0, right:0, bottom: 2});
38533         */
38534         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38535         
38536         
38537         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38538         
38539         this.autoScroll = c.autoScroll || false;
38540         
38541         
38542        
38543         
38544         this.duration = c.duration || .30;
38545         this.slideDuration = c.slideDuration || .45;
38546         this.config = c;
38547        
38548     },
38549     /**
38550      * Returns true if this region is currently visible.
38551      * @return {Boolean}
38552      */
38553     isVisible : function(){
38554         return this.visible;
38555     },
38556
38557     /**
38558      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38559      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38560      */
38561     //setCollapsedTitle : function(title){
38562     //    title = title || "&#160;";
38563      //   if(this.collapsedTitleTextEl){
38564       //      this.collapsedTitleTextEl.innerHTML = title;
38565        // }
38566     //},
38567
38568     getBox : function(){
38569         var b;
38570       //  if(!this.collapsed){
38571             b = this.el.getBox(false, true);
38572        // }else{
38573           //  b = this.collapsedEl.getBox(false, true);
38574         //}
38575         return b;
38576     },
38577
38578     getMargins : function(){
38579         return this.margins;
38580         //return this.collapsed ? this.cmargins : this.margins;
38581     },
38582 /*
38583     highlight : function(){
38584         this.el.addClass("x-layout-panel-dragover");
38585     },
38586
38587     unhighlight : function(){
38588         this.el.removeClass("x-layout-panel-dragover");
38589     },
38590 */
38591     updateBox : function(box)
38592     {
38593         if (!this.bodyEl) {
38594             return; // not rendered yet..
38595         }
38596         
38597         this.box = box;
38598         if(!this.collapsed){
38599             this.el.dom.style.left = box.x + "px";
38600             this.el.dom.style.top = box.y + "px";
38601             this.updateBody(box.width, box.height);
38602         }else{
38603             this.collapsedEl.dom.style.left = box.x + "px";
38604             this.collapsedEl.dom.style.top = box.y + "px";
38605             this.collapsedEl.setSize(box.width, box.height);
38606         }
38607         if(this.tabs){
38608             this.tabs.autoSizeTabs();
38609         }
38610     },
38611
38612     updateBody : function(w, h)
38613     {
38614         if(w !== null){
38615             this.el.setWidth(w);
38616             w -= this.el.getBorderWidth("rl");
38617             if(this.config.adjustments){
38618                 w += this.config.adjustments[0];
38619             }
38620         }
38621         if(h !== null && h > 0){
38622             this.el.setHeight(h);
38623             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38624             h -= this.el.getBorderWidth("tb");
38625             if(this.config.adjustments){
38626                 h += this.config.adjustments[1];
38627             }
38628             this.bodyEl.setHeight(h);
38629             if(this.tabs){
38630                 h = this.tabs.syncHeight(h);
38631             }
38632         }
38633         if(this.panelSize){
38634             w = w !== null ? w : this.panelSize.width;
38635             h = h !== null ? h : this.panelSize.height;
38636         }
38637         if(this.activePanel){
38638             var el = this.activePanel.getEl();
38639             w = w !== null ? w : el.getWidth();
38640             h = h !== null ? h : el.getHeight();
38641             this.panelSize = {width: w, height: h};
38642             this.activePanel.setSize(w, h);
38643         }
38644         if(Roo.isIE && this.tabs){
38645             this.tabs.el.repaint();
38646         }
38647     },
38648
38649     /**
38650      * Returns the container element for this region.
38651      * @return {Roo.Element}
38652      */
38653     getEl : function(){
38654         return this.el;
38655     },
38656
38657     /**
38658      * Hides this region.
38659      */
38660     hide : function(){
38661         //if(!this.collapsed){
38662             this.el.dom.style.left = "-2000px";
38663             this.el.hide();
38664         //}else{
38665          //   this.collapsedEl.dom.style.left = "-2000px";
38666          //   this.collapsedEl.hide();
38667        // }
38668         this.visible = false;
38669         this.fireEvent("visibilitychange", this, false);
38670     },
38671
38672     /**
38673      * Shows this region if it was previously hidden.
38674      */
38675     show : function(){
38676         //if(!this.collapsed){
38677             this.el.show();
38678         //}else{
38679         //    this.collapsedEl.show();
38680        // }
38681         this.visible = true;
38682         this.fireEvent("visibilitychange", this, true);
38683     },
38684 /*
38685     closeClicked : function(){
38686         if(this.activePanel){
38687             this.remove(this.activePanel);
38688         }
38689     },
38690
38691     collapseClick : function(e){
38692         if(this.isSlid){
38693            e.stopPropagation();
38694            this.slideIn();
38695         }else{
38696            e.stopPropagation();
38697            this.slideOut();
38698         }
38699     },
38700 */
38701     /**
38702      * Collapses this region.
38703      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38704      */
38705     /*
38706     collapse : function(skipAnim, skipCheck = false){
38707         if(this.collapsed) {
38708             return;
38709         }
38710         
38711         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38712             
38713             this.collapsed = true;
38714             if(this.split){
38715                 this.split.el.hide();
38716             }
38717             if(this.config.animate && skipAnim !== true){
38718                 this.fireEvent("invalidated", this);
38719                 this.animateCollapse();
38720             }else{
38721                 this.el.setLocation(-20000,-20000);
38722                 this.el.hide();
38723                 this.collapsedEl.show();
38724                 this.fireEvent("collapsed", this);
38725                 this.fireEvent("invalidated", this);
38726             }
38727         }
38728         
38729     },
38730 */
38731     animateCollapse : function(){
38732         // overridden
38733     },
38734
38735     /**
38736      * Expands this region if it was previously collapsed.
38737      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38738      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38739      */
38740     /*
38741     expand : function(e, skipAnim){
38742         if(e) {
38743             e.stopPropagation();
38744         }
38745         if(!this.collapsed || this.el.hasActiveFx()) {
38746             return;
38747         }
38748         if(this.isSlid){
38749             this.afterSlideIn();
38750             skipAnim = true;
38751         }
38752         this.collapsed = false;
38753         if(this.config.animate && skipAnim !== true){
38754             this.animateExpand();
38755         }else{
38756             this.el.show();
38757             if(this.split){
38758                 this.split.el.show();
38759             }
38760             this.collapsedEl.setLocation(-2000,-2000);
38761             this.collapsedEl.hide();
38762             this.fireEvent("invalidated", this);
38763             this.fireEvent("expanded", this);
38764         }
38765     },
38766 */
38767     animateExpand : function(){
38768         // overridden
38769     },
38770
38771     initTabs : function()
38772     {
38773         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38774         
38775         var ts = new Roo.bootstrap.panel.Tabs({
38776             el: this.bodyEl.dom,
38777             region : this,
38778             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38779             disableTooltips: this.config.disableTabTips,
38780             toolbar : this.config.toolbar
38781         });
38782         
38783         if(this.config.hideTabs){
38784             ts.stripWrap.setDisplayed(false);
38785         }
38786         this.tabs = ts;
38787         ts.resizeTabs = this.config.resizeTabs === true;
38788         ts.minTabWidth = this.config.minTabWidth || 40;
38789         ts.maxTabWidth = this.config.maxTabWidth || 250;
38790         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38791         ts.monitorResize = false;
38792         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38793         ts.bodyEl.addClass('roo-layout-tabs-body');
38794         this.panels.each(this.initPanelAsTab, this);
38795     },
38796
38797     initPanelAsTab : function(panel){
38798         var ti = this.tabs.addTab(
38799             panel.getEl().id,
38800             panel.getTitle(),
38801             null,
38802             this.config.closeOnTab && panel.isClosable(),
38803             panel.tpl
38804         );
38805         if(panel.tabTip !== undefined){
38806             ti.setTooltip(panel.tabTip);
38807         }
38808         ti.on("activate", function(){
38809               this.setActivePanel(panel);
38810         }, this);
38811         
38812         if(this.config.closeOnTab){
38813             ti.on("beforeclose", function(t, e){
38814                 e.cancel = true;
38815                 this.remove(panel);
38816             }, this);
38817         }
38818         
38819         panel.tabItem = ti;
38820         
38821         return ti;
38822     },
38823
38824     updatePanelTitle : function(panel, title)
38825     {
38826         if(this.activePanel == panel){
38827             this.updateTitle(title);
38828         }
38829         if(this.tabs){
38830             var ti = this.tabs.getTab(panel.getEl().id);
38831             ti.setText(title);
38832             if(panel.tabTip !== undefined){
38833                 ti.setTooltip(panel.tabTip);
38834             }
38835         }
38836     },
38837
38838     updateTitle : function(title){
38839         if(this.titleTextEl && !this.config.title){
38840             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38841         }
38842     },
38843
38844     setActivePanel : function(panel)
38845     {
38846         panel = this.getPanel(panel);
38847         if(this.activePanel && this.activePanel != panel){
38848             if(this.activePanel.setActiveState(false) === false){
38849                 return;
38850             }
38851         }
38852         this.activePanel = panel;
38853         panel.setActiveState(true);
38854         if(this.panelSize){
38855             panel.setSize(this.panelSize.width, this.panelSize.height);
38856         }
38857         if(this.closeBtn){
38858             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38859         }
38860         this.updateTitle(panel.getTitle());
38861         if(this.tabs){
38862             this.fireEvent("invalidated", this);
38863         }
38864         this.fireEvent("panelactivated", this, panel);
38865     },
38866
38867     /**
38868      * Shows the specified panel.
38869      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38870      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38871      */
38872     showPanel : function(panel)
38873     {
38874         panel = this.getPanel(panel);
38875         if(panel){
38876             if(this.tabs){
38877                 var tab = this.tabs.getTab(panel.getEl().id);
38878                 if(tab.isHidden()){
38879                     this.tabs.unhideTab(tab.id);
38880                 }
38881                 tab.activate();
38882             }else{
38883                 this.setActivePanel(panel);
38884             }
38885         }
38886         return panel;
38887     },
38888
38889     /**
38890      * Get the active panel for this region.
38891      * @return {Roo.ContentPanel} The active panel or null
38892      */
38893     getActivePanel : function(){
38894         return this.activePanel;
38895     },
38896
38897     validateVisibility : function(){
38898         if(this.panels.getCount() < 1){
38899             this.updateTitle("&#160;");
38900             this.closeBtn.hide();
38901             this.hide();
38902         }else{
38903             if(!this.isVisible()){
38904                 this.show();
38905             }
38906         }
38907     },
38908
38909     /**
38910      * Adds the passed ContentPanel(s) to this region.
38911      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38912      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38913      */
38914     add : function(panel)
38915     {
38916         if(arguments.length > 1){
38917             for(var i = 0, len = arguments.length; i < len; i++) {
38918                 this.add(arguments[i]);
38919             }
38920             return null;
38921         }
38922         
38923         // if we have not been rendered yet, then we can not really do much of this..
38924         if (!this.bodyEl) {
38925             this.unrendered_panels.push(panel);
38926             return panel;
38927         }
38928         
38929         
38930         
38931         
38932         if(this.hasPanel(panel)){
38933             this.showPanel(panel);
38934             return panel;
38935         }
38936         panel.setRegion(this);
38937         this.panels.add(panel);
38938        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38939             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38940             // and hide them... ???
38941             this.bodyEl.dom.appendChild(panel.getEl().dom);
38942             if(panel.background !== true){
38943                 this.setActivePanel(panel);
38944             }
38945             this.fireEvent("paneladded", this, panel);
38946             return panel;
38947         }
38948         */
38949         if(!this.tabs){
38950             this.initTabs();
38951         }else{
38952             this.initPanelAsTab(panel);
38953         }
38954         
38955         
38956         if(panel.background !== true){
38957             this.tabs.activate(panel.getEl().id);
38958         }
38959         this.fireEvent("paneladded", this, panel);
38960         return panel;
38961     },
38962
38963     /**
38964      * Hides the tab for the specified panel.
38965      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38966      */
38967     hidePanel : function(panel){
38968         if(this.tabs && (panel = this.getPanel(panel))){
38969             this.tabs.hideTab(panel.getEl().id);
38970         }
38971     },
38972
38973     /**
38974      * Unhides the tab for a previously hidden panel.
38975      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38976      */
38977     unhidePanel : function(panel){
38978         if(this.tabs && (panel = this.getPanel(panel))){
38979             this.tabs.unhideTab(panel.getEl().id);
38980         }
38981     },
38982
38983     clearPanels : function(){
38984         while(this.panels.getCount() > 0){
38985              this.remove(this.panels.first());
38986         }
38987     },
38988
38989     /**
38990      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38991      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38992      * @param {Boolean} preservePanel Overrides the config preservePanel option
38993      * @return {Roo.ContentPanel} The panel that was removed
38994      */
38995     remove : function(panel, preservePanel)
38996     {
38997         panel = this.getPanel(panel);
38998         if(!panel){
38999             return null;
39000         }
39001         var e = {};
39002         this.fireEvent("beforeremove", this, panel, e);
39003         if(e.cancel === true){
39004             return null;
39005         }
39006         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39007         var panelId = panel.getId();
39008         this.panels.removeKey(panelId);
39009         if(preservePanel){
39010             document.body.appendChild(panel.getEl().dom);
39011         }
39012         if(this.tabs){
39013             this.tabs.removeTab(panel.getEl().id);
39014         }else if (!preservePanel){
39015             this.bodyEl.dom.removeChild(panel.getEl().dom);
39016         }
39017         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39018             var p = this.panels.first();
39019             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39020             tempEl.appendChild(p.getEl().dom);
39021             this.bodyEl.update("");
39022             this.bodyEl.dom.appendChild(p.getEl().dom);
39023             tempEl = null;
39024             this.updateTitle(p.getTitle());
39025             this.tabs = null;
39026             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39027             this.setActivePanel(p);
39028         }
39029         panel.setRegion(null);
39030         if(this.activePanel == panel){
39031             this.activePanel = null;
39032         }
39033         if(this.config.autoDestroy !== false && preservePanel !== true){
39034             try{panel.destroy();}catch(e){}
39035         }
39036         this.fireEvent("panelremoved", this, panel);
39037         return panel;
39038     },
39039
39040     /**
39041      * Returns the TabPanel component used by this region
39042      * @return {Roo.TabPanel}
39043      */
39044     getTabs : function(){
39045         return this.tabs;
39046     },
39047
39048     createTool : function(parentEl, className){
39049         var btn = Roo.DomHelper.append(parentEl, {
39050             tag: "div",
39051             cls: "x-layout-tools-button",
39052             children: [ {
39053                 tag: "div",
39054                 cls: "roo-layout-tools-button-inner " + className,
39055                 html: "&#160;"
39056             }]
39057         }, true);
39058         btn.addClassOnOver("roo-layout-tools-button-over");
39059         return btn;
39060     }
39061 });/*
39062  * Based on:
39063  * Ext JS Library 1.1.1
39064  * Copyright(c) 2006-2007, Ext JS, LLC.
39065  *
39066  * Originally Released Under LGPL - original licence link has changed is not relivant.
39067  *
39068  * Fork - LGPL
39069  * <script type="text/javascript">
39070  */
39071  
39072
39073
39074 /**
39075  * @class Roo.SplitLayoutRegion
39076  * @extends Roo.LayoutRegion
39077  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39078  */
39079 Roo.bootstrap.layout.Split = function(config){
39080     this.cursor = config.cursor;
39081     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39082 };
39083
39084 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39085 {
39086     splitTip : "Drag to resize.",
39087     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39088     useSplitTips : false,
39089
39090     applyConfig : function(config){
39091         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39092     },
39093     
39094     onRender : function(ctr,pos) {
39095         
39096         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39097         if(!this.config.split){
39098             return;
39099         }
39100         if(!this.split){
39101             
39102             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39103                             tag: "div",
39104                             id: this.el.id + "-split",
39105                             cls: "roo-layout-split roo-layout-split-"+this.position,
39106                             html: "&#160;"
39107             });
39108             /** The SplitBar for this region 
39109             * @type Roo.SplitBar */
39110             // does not exist yet...
39111             Roo.log([this.position, this.orientation]);
39112             
39113             this.split = new Roo.bootstrap.SplitBar({
39114                 dragElement : splitEl,
39115                 resizingElement: this.el,
39116                 orientation : this.orientation
39117             });
39118             
39119             this.split.on("moved", this.onSplitMove, this);
39120             this.split.useShim = this.config.useShim === true;
39121             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39122             if(this.useSplitTips){
39123                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39124             }
39125             //if(config.collapsible){
39126             //    this.split.el.on("dblclick", this.collapse,  this);
39127             //}
39128         }
39129         if(typeof this.config.minSize != "undefined"){
39130             this.split.minSize = this.config.minSize;
39131         }
39132         if(typeof this.config.maxSize != "undefined"){
39133             this.split.maxSize = this.config.maxSize;
39134         }
39135         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39136             this.hideSplitter();
39137         }
39138         
39139     },
39140
39141     getHMaxSize : function(){
39142          var cmax = this.config.maxSize || 10000;
39143          var center = this.mgr.getRegion("center");
39144          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39145     },
39146
39147     getVMaxSize : function(){
39148          var cmax = this.config.maxSize || 10000;
39149          var center = this.mgr.getRegion("center");
39150          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39151     },
39152
39153     onSplitMove : function(split, newSize){
39154         this.fireEvent("resized", this, newSize);
39155     },
39156     
39157     /** 
39158      * Returns the {@link Roo.SplitBar} for this region.
39159      * @return {Roo.SplitBar}
39160      */
39161     getSplitBar : function(){
39162         return this.split;
39163     },
39164     
39165     hide : function(){
39166         this.hideSplitter();
39167         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39168     },
39169
39170     hideSplitter : function(){
39171         if(this.split){
39172             this.split.el.setLocation(-2000,-2000);
39173             this.split.el.hide();
39174         }
39175     },
39176
39177     show : function(){
39178         if(this.split){
39179             this.split.el.show();
39180         }
39181         Roo.bootstrap.layout.Split.superclass.show.call(this);
39182     },
39183     
39184     beforeSlide: function(){
39185         if(Roo.isGecko){// firefox overflow auto bug workaround
39186             this.bodyEl.clip();
39187             if(this.tabs) {
39188                 this.tabs.bodyEl.clip();
39189             }
39190             if(this.activePanel){
39191                 this.activePanel.getEl().clip();
39192                 
39193                 if(this.activePanel.beforeSlide){
39194                     this.activePanel.beforeSlide();
39195                 }
39196             }
39197         }
39198     },
39199     
39200     afterSlide : function(){
39201         if(Roo.isGecko){// firefox overflow auto bug workaround
39202             this.bodyEl.unclip();
39203             if(this.tabs) {
39204                 this.tabs.bodyEl.unclip();
39205             }
39206             if(this.activePanel){
39207                 this.activePanel.getEl().unclip();
39208                 if(this.activePanel.afterSlide){
39209                     this.activePanel.afterSlide();
39210                 }
39211             }
39212         }
39213     },
39214
39215     initAutoHide : function(){
39216         if(this.autoHide !== false){
39217             if(!this.autoHideHd){
39218                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39219                 this.autoHideHd = {
39220                     "mouseout": function(e){
39221                         if(!e.within(this.el, true)){
39222                             st.delay(500);
39223                         }
39224                     },
39225                     "mouseover" : function(e){
39226                         st.cancel();
39227                     },
39228                     scope : this
39229                 };
39230             }
39231             this.el.on(this.autoHideHd);
39232         }
39233     },
39234
39235     clearAutoHide : function(){
39236         if(this.autoHide !== false){
39237             this.el.un("mouseout", this.autoHideHd.mouseout);
39238             this.el.un("mouseover", this.autoHideHd.mouseover);
39239         }
39240     },
39241
39242     clearMonitor : function(){
39243         Roo.get(document).un("click", this.slideInIf, this);
39244     },
39245
39246     // these names are backwards but not changed for compat
39247     slideOut : function(){
39248         if(this.isSlid || this.el.hasActiveFx()){
39249             return;
39250         }
39251         this.isSlid = true;
39252         if(this.collapseBtn){
39253             this.collapseBtn.hide();
39254         }
39255         this.closeBtnState = this.closeBtn.getStyle('display');
39256         this.closeBtn.hide();
39257         if(this.stickBtn){
39258             this.stickBtn.show();
39259         }
39260         this.el.show();
39261         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39262         this.beforeSlide();
39263         this.el.setStyle("z-index", 10001);
39264         this.el.slideIn(this.getSlideAnchor(), {
39265             callback: function(){
39266                 this.afterSlide();
39267                 this.initAutoHide();
39268                 Roo.get(document).on("click", this.slideInIf, this);
39269                 this.fireEvent("slideshow", this);
39270             },
39271             scope: this,
39272             block: true
39273         });
39274     },
39275
39276     afterSlideIn : function(){
39277         this.clearAutoHide();
39278         this.isSlid = false;
39279         this.clearMonitor();
39280         this.el.setStyle("z-index", "");
39281         if(this.collapseBtn){
39282             this.collapseBtn.show();
39283         }
39284         this.closeBtn.setStyle('display', this.closeBtnState);
39285         if(this.stickBtn){
39286             this.stickBtn.hide();
39287         }
39288         this.fireEvent("slidehide", this);
39289     },
39290
39291     slideIn : function(cb){
39292         if(!this.isSlid || this.el.hasActiveFx()){
39293             Roo.callback(cb);
39294             return;
39295         }
39296         this.isSlid = false;
39297         this.beforeSlide();
39298         this.el.slideOut(this.getSlideAnchor(), {
39299             callback: function(){
39300                 this.el.setLeftTop(-10000, -10000);
39301                 this.afterSlide();
39302                 this.afterSlideIn();
39303                 Roo.callback(cb);
39304             },
39305             scope: this,
39306             block: true
39307         });
39308     },
39309     
39310     slideInIf : function(e){
39311         if(!e.within(this.el)){
39312             this.slideIn();
39313         }
39314     },
39315
39316     animateCollapse : function(){
39317         this.beforeSlide();
39318         this.el.setStyle("z-index", 20000);
39319         var anchor = this.getSlideAnchor();
39320         this.el.slideOut(anchor, {
39321             callback : function(){
39322                 this.el.setStyle("z-index", "");
39323                 this.collapsedEl.slideIn(anchor, {duration:.3});
39324                 this.afterSlide();
39325                 this.el.setLocation(-10000,-10000);
39326                 this.el.hide();
39327                 this.fireEvent("collapsed", this);
39328             },
39329             scope: this,
39330             block: true
39331         });
39332     },
39333
39334     animateExpand : function(){
39335         this.beforeSlide();
39336         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39337         this.el.setStyle("z-index", 20000);
39338         this.collapsedEl.hide({
39339             duration:.1
39340         });
39341         this.el.slideIn(this.getSlideAnchor(), {
39342             callback : function(){
39343                 this.el.setStyle("z-index", "");
39344                 this.afterSlide();
39345                 if(this.split){
39346                     this.split.el.show();
39347                 }
39348                 this.fireEvent("invalidated", this);
39349                 this.fireEvent("expanded", this);
39350             },
39351             scope: this,
39352             block: true
39353         });
39354     },
39355
39356     anchors : {
39357         "west" : "left",
39358         "east" : "right",
39359         "north" : "top",
39360         "south" : "bottom"
39361     },
39362
39363     sanchors : {
39364         "west" : "l",
39365         "east" : "r",
39366         "north" : "t",
39367         "south" : "b"
39368     },
39369
39370     canchors : {
39371         "west" : "tl-tr",
39372         "east" : "tr-tl",
39373         "north" : "tl-bl",
39374         "south" : "bl-tl"
39375     },
39376
39377     getAnchor : function(){
39378         return this.anchors[this.position];
39379     },
39380
39381     getCollapseAnchor : function(){
39382         return this.canchors[this.position];
39383     },
39384
39385     getSlideAnchor : function(){
39386         return this.sanchors[this.position];
39387     },
39388
39389     getAlignAdj : function(){
39390         var cm = this.cmargins;
39391         switch(this.position){
39392             case "west":
39393                 return [0, 0];
39394             break;
39395             case "east":
39396                 return [0, 0];
39397             break;
39398             case "north":
39399                 return [0, 0];
39400             break;
39401             case "south":
39402                 return [0, 0];
39403             break;
39404         }
39405     },
39406
39407     getExpandAdj : function(){
39408         var c = this.collapsedEl, cm = this.cmargins;
39409         switch(this.position){
39410             case "west":
39411                 return [-(cm.right+c.getWidth()+cm.left), 0];
39412             break;
39413             case "east":
39414                 return [cm.right+c.getWidth()+cm.left, 0];
39415             break;
39416             case "north":
39417                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39418             break;
39419             case "south":
39420                 return [0, cm.top+cm.bottom+c.getHeight()];
39421             break;
39422         }
39423     }
39424 });/*
39425  * Based on:
39426  * Ext JS Library 1.1.1
39427  * Copyright(c) 2006-2007, Ext JS, LLC.
39428  *
39429  * Originally Released Under LGPL - original licence link has changed is not relivant.
39430  *
39431  * Fork - LGPL
39432  * <script type="text/javascript">
39433  */
39434 /*
39435  * These classes are private internal classes
39436  */
39437 Roo.bootstrap.layout.Center = function(config){
39438     config.region = "center";
39439     Roo.bootstrap.layout.Region.call(this, config);
39440     this.visible = true;
39441     this.minWidth = config.minWidth || 20;
39442     this.minHeight = config.minHeight || 20;
39443 };
39444
39445 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39446     hide : function(){
39447         // center panel can't be hidden
39448     },
39449     
39450     show : function(){
39451         // center panel can't be hidden
39452     },
39453     
39454     getMinWidth: function(){
39455         return this.minWidth;
39456     },
39457     
39458     getMinHeight: function(){
39459         return this.minHeight;
39460     }
39461 });
39462
39463
39464
39465
39466  
39467
39468
39469
39470
39471
39472
39473 Roo.bootstrap.layout.North = function(config)
39474 {
39475     config.region = 'north';
39476     config.cursor = 'n-resize';
39477     
39478     Roo.bootstrap.layout.Split.call(this, config);
39479     
39480     
39481     if(this.split){
39482         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39483         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39484         this.split.el.addClass("roo-layout-split-v");
39485     }
39486     //var size = config.initialSize || config.height;
39487     //if(this.el && typeof size != "undefined"){
39488     //    this.el.setHeight(size);
39489     //}
39490 };
39491 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39492 {
39493     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39494      
39495      
39496     onRender : function(ctr, pos)
39497     {
39498         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39499         var size = this.config.initialSize || this.config.height;
39500         if(this.el && typeof size != "undefined"){
39501             this.el.setHeight(size);
39502         }
39503     
39504     },
39505     
39506     getBox : function(){
39507         if(this.collapsed){
39508             return this.collapsedEl.getBox();
39509         }
39510         var box = this.el.getBox();
39511         if(this.split){
39512             box.height += this.split.el.getHeight();
39513         }
39514         return box;
39515     },
39516     
39517     updateBox : function(box){
39518         if(this.split && !this.collapsed){
39519             box.height -= this.split.el.getHeight();
39520             this.split.el.setLeft(box.x);
39521             this.split.el.setTop(box.y+box.height);
39522             this.split.el.setWidth(box.width);
39523         }
39524         if(this.collapsed){
39525             this.updateBody(box.width, null);
39526         }
39527         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39528     }
39529 });
39530
39531
39532
39533
39534
39535 Roo.bootstrap.layout.South = function(config){
39536     config.region = 'south';
39537     config.cursor = 's-resize';
39538     Roo.bootstrap.layout.Split.call(this, config);
39539     if(this.split){
39540         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39541         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39542         this.split.el.addClass("roo-layout-split-v");
39543     }
39544     
39545 };
39546
39547 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39548     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39549     
39550     onRender : function(ctr, pos)
39551     {
39552         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39553         var size = this.config.initialSize || this.config.height;
39554         if(this.el && typeof size != "undefined"){
39555             this.el.setHeight(size);
39556         }
39557     
39558     },
39559     
39560     getBox : function(){
39561         if(this.collapsed){
39562             return this.collapsedEl.getBox();
39563         }
39564         var box = this.el.getBox();
39565         if(this.split){
39566             var sh = this.split.el.getHeight();
39567             box.height += sh;
39568             box.y -= sh;
39569         }
39570         return box;
39571     },
39572     
39573     updateBox : function(box){
39574         if(this.split && !this.collapsed){
39575             var sh = this.split.el.getHeight();
39576             box.height -= sh;
39577             box.y += sh;
39578             this.split.el.setLeft(box.x);
39579             this.split.el.setTop(box.y-sh);
39580             this.split.el.setWidth(box.width);
39581         }
39582         if(this.collapsed){
39583             this.updateBody(box.width, null);
39584         }
39585         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39586     }
39587 });
39588
39589 Roo.bootstrap.layout.East = function(config){
39590     config.region = "east";
39591     config.cursor = "e-resize";
39592     Roo.bootstrap.layout.Split.call(this, config);
39593     if(this.split){
39594         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39595         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39596         this.split.el.addClass("roo-layout-split-h");
39597     }
39598     
39599 };
39600 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39601     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39602     
39603     onRender : function(ctr, pos)
39604     {
39605         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39606         var size = this.config.initialSize || this.config.width;
39607         if(this.el && typeof size != "undefined"){
39608             this.el.setWidth(size);
39609         }
39610     
39611     },
39612     
39613     getBox : function(){
39614         if(this.collapsed){
39615             return this.collapsedEl.getBox();
39616         }
39617         var box = this.el.getBox();
39618         if(this.split){
39619             var sw = this.split.el.getWidth();
39620             box.width += sw;
39621             box.x -= sw;
39622         }
39623         return box;
39624     },
39625
39626     updateBox : function(box){
39627         if(this.split && !this.collapsed){
39628             var sw = this.split.el.getWidth();
39629             box.width -= sw;
39630             this.split.el.setLeft(box.x);
39631             this.split.el.setTop(box.y);
39632             this.split.el.setHeight(box.height);
39633             box.x += sw;
39634         }
39635         if(this.collapsed){
39636             this.updateBody(null, box.height);
39637         }
39638         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39639     }
39640 });
39641
39642 Roo.bootstrap.layout.West = function(config){
39643     config.region = "west";
39644     config.cursor = "w-resize";
39645     
39646     Roo.bootstrap.layout.Split.call(this, config);
39647     if(this.split){
39648         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39649         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39650         this.split.el.addClass("roo-layout-split-h");
39651     }
39652     
39653 };
39654 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39655     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39656     
39657     onRender: function(ctr, pos)
39658     {
39659         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39660         var size = this.config.initialSize || this.config.width;
39661         if(typeof size != "undefined"){
39662             this.el.setWidth(size);
39663         }
39664     },
39665     
39666     getBox : function(){
39667         if(this.collapsed){
39668             return this.collapsedEl.getBox();
39669         }
39670         var box = this.el.getBox();
39671         if (box.width == 0) {
39672             box.width = this.config.width; // kludge?
39673         }
39674         if(this.split){
39675             box.width += this.split.el.getWidth();
39676         }
39677         return box;
39678     },
39679     
39680     updateBox : function(box){
39681         if(this.split && !this.collapsed){
39682             var sw = this.split.el.getWidth();
39683             box.width -= sw;
39684             this.split.el.setLeft(box.x+box.width);
39685             this.split.el.setTop(box.y);
39686             this.split.el.setHeight(box.height);
39687         }
39688         if(this.collapsed){
39689             this.updateBody(null, box.height);
39690         }
39691         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39692     }
39693 });Roo.namespace("Roo.bootstrap.panel");/*
39694  * Based on:
39695  * Ext JS Library 1.1.1
39696  * Copyright(c) 2006-2007, Ext JS, LLC.
39697  *
39698  * Originally Released Under LGPL - original licence link has changed is not relivant.
39699  *
39700  * Fork - LGPL
39701  * <script type="text/javascript">
39702  */
39703 /**
39704  * @class Roo.ContentPanel
39705  * @extends Roo.util.Observable
39706  * A basic ContentPanel element.
39707  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39708  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39709  * @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
39710  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39711  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39712  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39713  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39714  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39715  * @cfg {String} title          The title for this panel
39716  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39717  * @cfg {String} url            Calls {@link #setUrl} with this value
39718  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39719  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39720  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39721  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39722  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39723  * @cfg {Boolean} badges render the badges
39724  * @cfg {String} cls  extra classes to use  
39725  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39726
39727  * @constructor
39728  * Create a new ContentPanel.
39729  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39730  * @param {String/Object} config A string to set only the title or a config object
39731  * @param {String} content (optional) Set the HTML content for this panel
39732  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39733  */
39734 Roo.bootstrap.panel.Content = function( config){
39735     
39736     this.tpl = config.tpl || false;
39737     
39738     var el = config.el;
39739     var content = config.content;
39740
39741     if(config.autoCreate){ // xtype is available if this is called from factory
39742         el = Roo.id();
39743     }
39744     this.el = Roo.get(el);
39745     if(!this.el && config && config.autoCreate){
39746         if(typeof config.autoCreate == "object"){
39747             if(!config.autoCreate.id){
39748                 config.autoCreate.id = config.id||el;
39749             }
39750             this.el = Roo.DomHelper.append(document.body,
39751                         config.autoCreate, true);
39752         }else{
39753             var elcfg =  {
39754                 tag: "div",
39755                 cls: (config.cls || '') +
39756                     (config.background ? ' bg-' + config.background : '') +
39757                     " roo-layout-inactive-content",
39758                 id: config.id||el
39759             };
39760             if (config.iframe) {
39761                 elcfg.cn = [
39762                     {
39763                         tag : 'iframe',
39764                         style : 'border: 0px',
39765                         src : 'about:blank'
39766                     }
39767                 ];
39768             }
39769               
39770             if (config.html) {
39771                 elcfg.html = config.html;
39772                 
39773             }
39774                         
39775             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39776             if (config.iframe) {
39777                 this.iframeEl = this.el.select('iframe',true).first();
39778             }
39779             
39780         }
39781     } 
39782     this.closable = false;
39783     this.loaded = false;
39784     this.active = false;
39785    
39786       
39787     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39788         
39789         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39790         
39791         this.wrapEl = this.el; //this.el.wrap();
39792         var ti = [];
39793         if (config.toolbar.items) {
39794             ti = config.toolbar.items ;
39795             delete config.toolbar.items ;
39796         }
39797         
39798         var nitems = [];
39799         this.toolbar.render(this.wrapEl, 'before');
39800         for(var i =0;i < ti.length;i++) {
39801           //  Roo.log(['add child', items[i]]);
39802             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39803         }
39804         this.toolbar.items = nitems;
39805         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39806         delete config.toolbar;
39807         
39808     }
39809     /*
39810     // xtype created footer. - not sure if will work as we normally have to render first..
39811     if (this.footer && !this.footer.el && this.footer.xtype) {
39812         if (!this.wrapEl) {
39813             this.wrapEl = this.el.wrap();
39814         }
39815     
39816         this.footer.container = this.wrapEl.createChild();
39817          
39818         this.footer = Roo.factory(this.footer, Roo);
39819         
39820     }
39821     */
39822     
39823      if(typeof config == "string"){
39824         this.title = config;
39825     }else{
39826         Roo.apply(this, config);
39827     }
39828     
39829     if(this.resizeEl){
39830         this.resizeEl = Roo.get(this.resizeEl, true);
39831     }else{
39832         this.resizeEl = this.el;
39833     }
39834     // handle view.xtype
39835     
39836  
39837     
39838     
39839     this.addEvents({
39840         /**
39841          * @event activate
39842          * Fires when this panel is activated. 
39843          * @param {Roo.ContentPanel} this
39844          */
39845         "activate" : true,
39846         /**
39847          * @event deactivate
39848          * Fires when this panel is activated. 
39849          * @param {Roo.ContentPanel} this
39850          */
39851         "deactivate" : true,
39852
39853         /**
39854          * @event resize
39855          * Fires when this panel is resized if fitToFrame is true.
39856          * @param {Roo.ContentPanel} this
39857          * @param {Number} width The width after any component adjustments
39858          * @param {Number} height The height after any component adjustments
39859          */
39860         "resize" : true,
39861         
39862          /**
39863          * @event render
39864          * Fires when this tab is created
39865          * @param {Roo.ContentPanel} this
39866          */
39867         "render" : true
39868         
39869         
39870         
39871     });
39872     
39873
39874     
39875     
39876     if(this.autoScroll && !this.iframe){
39877         this.resizeEl.setStyle("overflow", "auto");
39878     } else {
39879         // fix randome scrolling
39880         //this.el.on('scroll', function() {
39881         //    Roo.log('fix random scolling');
39882         //    this.scrollTo('top',0); 
39883         //});
39884     }
39885     content = content || this.content;
39886     if(content){
39887         this.setContent(content);
39888     }
39889     if(config && config.url){
39890         this.setUrl(this.url, this.params, this.loadOnce);
39891     }
39892     
39893     
39894     
39895     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39896     
39897     if (this.view && typeof(this.view.xtype) != 'undefined') {
39898         this.view.el = this.el.appendChild(document.createElement("div"));
39899         this.view = Roo.factory(this.view); 
39900         this.view.render  &&  this.view.render(false, '');  
39901     }
39902     
39903     
39904     this.fireEvent('render', this);
39905 };
39906
39907 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39908     
39909     cls : '',
39910     background : '',
39911     
39912     tabTip : '',
39913     
39914     iframe : false,
39915     iframeEl : false,
39916     
39917     setRegion : function(region){
39918         this.region = region;
39919         this.setActiveClass(region && !this.background);
39920     },
39921     
39922     
39923     setActiveClass: function(state)
39924     {
39925         if(state){
39926            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39927            this.el.setStyle('position','relative');
39928         }else{
39929            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39930            this.el.setStyle('position', 'absolute');
39931         } 
39932     },
39933     
39934     /**
39935      * Returns the toolbar for this Panel if one was configured. 
39936      * @return {Roo.Toolbar} 
39937      */
39938     getToolbar : function(){
39939         return this.toolbar;
39940     },
39941     
39942     setActiveState : function(active)
39943     {
39944         this.active = active;
39945         this.setActiveClass(active);
39946         if(!active){
39947             if(this.fireEvent("deactivate", this) === false){
39948                 return false;
39949             }
39950             return true;
39951         }
39952         this.fireEvent("activate", this);
39953         return true;
39954     },
39955     /**
39956      * Updates this panel's element (not for iframe)
39957      * @param {String} content The new content
39958      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39959     */
39960     setContent : function(content, loadScripts){
39961         if (this.iframe) {
39962             return;
39963         }
39964         
39965         this.el.update(content, loadScripts);
39966     },
39967
39968     ignoreResize : function(w, h){
39969         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39970             return true;
39971         }else{
39972             this.lastSize = {width: w, height: h};
39973             return false;
39974         }
39975     },
39976     /**
39977      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39978      * @return {Roo.UpdateManager} The UpdateManager
39979      */
39980     getUpdateManager : function(){
39981         if (this.iframe) {
39982             return false;
39983         }
39984         return this.el.getUpdateManager();
39985     },
39986      /**
39987      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39988      * Does not work with IFRAME contents
39989      * @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:
39990 <pre><code>
39991 panel.load({
39992     url: "your-url.php",
39993     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39994     callback: yourFunction,
39995     scope: yourObject, //(optional scope)
39996     discardUrl: false,
39997     nocache: false,
39998     text: "Loading...",
39999     timeout: 30,
40000     scripts: false
40001 });
40002 </code></pre>
40003      
40004      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40005      * 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.
40006      * @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}
40007      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40008      * @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.
40009      * @return {Roo.ContentPanel} this
40010      */
40011     load : function(){
40012         
40013         if (this.iframe) {
40014             return this;
40015         }
40016         
40017         var um = this.el.getUpdateManager();
40018         um.update.apply(um, arguments);
40019         return this;
40020     },
40021
40022
40023     /**
40024      * 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.
40025      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40026      * @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)
40027      * @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)
40028      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40029      */
40030     setUrl : function(url, params, loadOnce){
40031         if (this.iframe) {
40032             this.iframeEl.dom.src = url;
40033             return false;
40034         }
40035         
40036         if(this.refreshDelegate){
40037             this.removeListener("activate", this.refreshDelegate);
40038         }
40039         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40040         this.on("activate", this.refreshDelegate);
40041         return this.el.getUpdateManager();
40042     },
40043     
40044     _handleRefresh : function(url, params, loadOnce){
40045         if(!loadOnce || !this.loaded){
40046             var updater = this.el.getUpdateManager();
40047             updater.update(url, params, this._setLoaded.createDelegate(this));
40048         }
40049     },
40050     
40051     _setLoaded : function(){
40052         this.loaded = true;
40053     }, 
40054     
40055     /**
40056      * Returns this panel's id
40057      * @return {String} 
40058      */
40059     getId : function(){
40060         return this.el.id;
40061     },
40062     
40063     /** 
40064      * Returns this panel's element - used by regiosn to add.
40065      * @return {Roo.Element} 
40066      */
40067     getEl : function(){
40068         return this.wrapEl || this.el;
40069     },
40070     
40071    
40072     
40073     adjustForComponents : function(width, height)
40074     {
40075         //Roo.log('adjustForComponents ');
40076         if(this.resizeEl != this.el){
40077             width -= this.el.getFrameWidth('lr');
40078             height -= this.el.getFrameWidth('tb');
40079         }
40080         if(this.toolbar){
40081             var te = this.toolbar.getEl();
40082             te.setWidth(width);
40083             height -= te.getHeight();
40084         }
40085         if(this.footer){
40086             var te = this.footer.getEl();
40087             te.setWidth(width);
40088             height -= te.getHeight();
40089         }
40090         
40091         
40092         if(this.adjustments){
40093             width += this.adjustments[0];
40094             height += this.adjustments[1];
40095         }
40096         return {"width": width, "height": height};
40097     },
40098     
40099     setSize : function(width, height){
40100         if(this.fitToFrame && !this.ignoreResize(width, height)){
40101             if(this.fitContainer && this.resizeEl != this.el){
40102                 this.el.setSize(width, height);
40103             }
40104             var size = this.adjustForComponents(width, height);
40105             if (this.iframe) {
40106                 this.iframeEl.setSize(width,height);
40107             }
40108             
40109             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40110             this.fireEvent('resize', this, size.width, size.height);
40111             
40112             
40113         }
40114     },
40115     
40116     /**
40117      * Returns this panel's title
40118      * @return {String} 
40119      */
40120     getTitle : function(){
40121         
40122         if (typeof(this.title) != 'object') {
40123             return this.title;
40124         }
40125         
40126         var t = '';
40127         for (var k in this.title) {
40128             if (!this.title.hasOwnProperty(k)) {
40129                 continue;
40130             }
40131             
40132             if (k.indexOf('-') >= 0) {
40133                 var s = k.split('-');
40134                 for (var i = 0; i<s.length; i++) {
40135                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40136                 }
40137             } else {
40138                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40139             }
40140         }
40141         return t;
40142     },
40143     
40144     /**
40145      * Set this panel's title
40146      * @param {String} title
40147      */
40148     setTitle : function(title){
40149         this.title = title;
40150         if(this.region){
40151             this.region.updatePanelTitle(this, title);
40152         }
40153     },
40154     
40155     /**
40156      * Returns true is this panel was configured to be closable
40157      * @return {Boolean} 
40158      */
40159     isClosable : function(){
40160         return this.closable;
40161     },
40162     
40163     beforeSlide : function(){
40164         this.el.clip();
40165         this.resizeEl.clip();
40166     },
40167     
40168     afterSlide : function(){
40169         this.el.unclip();
40170         this.resizeEl.unclip();
40171     },
40172     
40173     /**
40174      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40175      *   Will fail silently if the {@link #setUrl} method has not been called.
40176      *   This does not activate the panel, just updates its content.
40177      */
40178     refresh : function(){
40179         if(this.refreshDelegate){
40180            this.loaded = false;
40181            this.refreshDelegate();
40182         }
40183     },
40184     
40185     /**
40186      * Destroys this panel
40187      */
40188     destroy : function(){
40189         this.el.removeAllListeners();
40190         var tempEl = document.createElement("span");
40191         tempEl.appendChild(this.el.dom);
40192         tempEl.innerHTML = "";
40193         this.el.remove();
40194         this.el = null;
40195     },
40196     
40197     /**
40198      * form - if the content panel contains a form - this is a reference to it.
40199      * @type {Roo.form.Form}
40200      */
40201     form : false,
40202     /**
40203      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40204      *    This contains a reference to it.
40205      * @type {Roo.View}
40206      */
40207     view : false,
40208     
40209       /**
40210      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40211      * <pre><code>
40212
40213 layout.addxtype({
40214        xtype : 'Form',
40215        items: [ .... ]
40216    }
40217 );
40218
40219 </code></pre>
40220      * @param {Object} cfg Xtype definition of item to add.
40221      */
40222     
40223     
40224     getChildContainer: function () {
40225         return this.getEl();
40226     }
40227     
40228     
40229     /*
40230         var  ret = new Roo.factory(cfg);
40231         return ret;
40232         
40233         
40234         // add form..
40235         if (cfg.xtype.match(/^Form$/)) {
40236             
40237             var el;
40238             //if (this.footer) {
40239             //    el = this.footer.container.insertSibling(false, 'before');
40240             //} else {
40241                 el = this.el.createChild();
40242             //}
40243
40244             this.form = new  Roo.form.Form(cfg);
40245             
40246             
40247             if ( this.form.allItems.length) {
40248                 this.form.render(el.dom);
40249             }
40250             return this.form;
40251         }
40252         // should only have one of theses..
40253         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40254             // views.. should not be just added - used named prop 'view''
40255             
40256             cfg.el = this.el.appendChild(document.createElement("div"));
40257             // factory?
40258             
40259             var ret = new Roo.factory(cfg);
40260              
40261              ret.render && ret.render(false, ''); // render blank..
40262             this.view = ret;
40263             return ret;
40264         }
40265         return false;
40266     }
40267     \*/
40268 });
40269  
40270 /**
40271  * @class Roo.bootstrap.panel.Grid
40272  * @extends Roo.bootstrap.panel.Content
40273  * @constructor
40274  * Create a new GridPanel.
40275  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40276  * @param {Object} config A the config object
40277   
40278  */
40279
40280
40281
40282 Roo.bootstrap.panel.Grid = function(config)
40283 {
40284     
40285       
40286     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40287         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40288
40289     config.el = this.wrapper;
40290     //this.el = this.wrapper;
40291     
40292       if (config.container) {
40293         // ctor'ed from a Border/panel.grid
40294         
40295         
40296         this.wrapper.setStyle("overflow", "hidden");
40297         this.wrapper.addClass('roo-grid-container');
40298
40299     }
40300     
40301     
40302     if(config.toolbar){
40303         var tool_el = this.wrapper.createChild();    
40304         this.toolbar = Roo.factory(config.toolbar);
40305         var ti = [];
40306         if (config.toolbar.items) {
40307             ti = config.toolbar.items ;
40308             delete config.toolbar.items ;
40309         }
40310         
40311         var nitems = [];
40312         this.toolbar.render(tool_el);
40313         for(var i =0;i < ti.length;i++) {
40314           //  Roo.log(['add child', items[i]]);
40315             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40316         }
40317         this.toolbar.items = nitems;
40318         
40319         delete config.toolbar;
40320     }
40321     
40322     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40323     config.grid.scrollBody = true;;
40324     config.grid.monitorWindowResize = false; // turn off autosizing
40325     config.grid.autoHeight = false;
40326     config.grid.autoWidth = false;
40327     
40328     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40329     
40330     if (config.background) {
40331         // render grid on panel activation (if panel background)
40332         this.on('activate', function(gp) {
40333             if (!gp.grid.rendered) {
40334                 gp.grid.render(this.wrapper);
40335                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40336             }
40337         });
40338             
40339     } else {
40340         this.grid.render(this.wrapper);
40341         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40342
40343     }
40344     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40345     // ??? needed ??? config.el = this.wrapper;
40346     
40347     
40348     
40349   
40350     // xtype created footer. - not sure if will work as we normally have to render first..
40351     if (this.footer && !this.footer.el && this.footer.xtype) {
40352         
40353         var ctr = this.grid.getView().getFooterPanel(true);
40354         this.footer.dataSource = this.grid.dataSource;
40355         this.footer = Roo.factory(this.footer, Roo);
40356         this.footer.render(ctr);
40357         
40358     }
40359     
40360     
40361     
40362     
40363      
40364 };
40365
40366 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40367     getId : function(){
40368         return this.grid.id;
40369     },
40370     
40371     /**
40372      * Returns the grid for this panel
40373      * @return {Roo.bootstrap.Table} 
40374      */
40375     getGrid : function(){
40376         return this.grid;    
40377     },
40378     
40379     setSize : function(width, height){
40380         if(!this.ignoreResize(width, height)){
40381             var grid = this.grid;
40382             var size = this.adjustForComponents(width, height);
40383             // tfoot is not a footer?
40384           
40385             
40386             var gridel = grid.getGridEl();
40387             gridel.setSize(size.width, size.height);
40388             
40389             var tbd = grid.getGridEl().select('tbody', true).first();
40390             var thd = grid.getGridEl().select('thead',true).first();
40391             var tbf= grid.getGridEl().select('tfoot', true).first();
40392
40393             if (tbf) {
40394                 size.height -= tbf.getHeight();
40395             }
40396             if (thd) {
40397                 size.height -= thd.getHeight();
40398             }
40399             
40400             tbd.setSize(size.width, size.height );
40401             // this is for the account management tab -seems to work there.
40402             var thd = grid.getGridEl().select('thead',true).first();
40403             //if (tbd) {
40404             //    tbd.setSize(size.width, size.height - thd.getHeight());
40405             //}
40406              
40407             grid.autoSize();
40408         }
40409     },
40410      
40411     
40412     
40413     beforeSlide : function(){
40414         this.grid.getView().scroller.clip();
40415     },
40416     
40417     afterSlide : function(){
40418         this.grid.getView().scroller.unclip();
40419     },
40420     
40421     destroy : function(){
40422         this.grid.destroy();
40423         delete this.grid;
40424         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40425     }
40426 });
40427
40428 /**
40429  * @class Roo.bootstrap.panel.Nest
40430  * @extends Roo.bootstrap.panel.Content
40431  * @constructor
40432  * Create a new Panel, that can contain a layout.Border.
40433  * 
40434  * 
40435  * @param {Roo.BorderLayout} layout The layout for this panel
40436  * @param {String/Object} config A string to set only the title or a config object
40437  */
40438 Roo.bootstrap.panel.Nest = function(config)
40439 {
40440     // construct with only one argument..
40441     /* FIXME - implement nicer consturctors
40442     if (layout.layout) {
40443         config = layout;
40444         layout = config.layout;
40445         delete config.layout;
40446     }
40447     if (layout.xtype && !layout.getEl) {
40448         // then layout needs constructing..
40449         layout = Roo.factory(layout, Roo);
40450     }
40451     */
40452     
40453     config.el =  config.layout.getEl();
40454     
40455     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40456     
40457     config.layout.monitorWindowResize = false; // turn off autosizing
40458     this.layout = config.layout;
40459     this.layout.getEl().addClass("roo-layout-nested-layout");
40460     this.layout.parent = this;
40461     
40462     
40463     
40464     
40465 };
40466
40467 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40468
40469     setSize : function(width, height){
40470         if(!this.ignoreResize(width, height)){
40471             var size = this.adjustForComponents(width, height);
40472             var el = this.layout.getEl();
40473             if (size.height < 1) {
40474                 el.setWidth(size.width);   
40475             } else {
40476                 el.setSize(size.width, size.height);
40477             }
40478             var touch = el.dom.offsetWidth;
40479             this.layout.layout();
40480             // ie requires a double layout on the first pass
40481             if(Roo.isIE && !this.initialized){
40482                 this.initialized = true;
40483                 this.layout.layout();
40484             }
40485         }
40486     },
40487     
40488     // activate all subpanels if not currently active..
40489     
40490     setActiveState : function(active){
40491         this.active = active;
40492         this.setActiveClass(active);
40493         
40494         if(!active){
40495             this.fireEvent("deactivate", this);
40496             return;
40497         }
40498         
40499         this.fireEvent("activate", this);
40500         // not sure if this should happen before or after..
40501         if (!this.layout) {
40502             return; // should not happen..
40503         }
40504         var reg = false;
40505         for (var r in this.layout.regions) {
40506             reg = this.layout.getRegion(r);
40507             if (reg.getActivePanel()) {
40508                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40509                 reg.setActivePanel(reg.getActivePanel());
40510                 continue;
40511             }
40512             if (!reg.panels.length) {
40513                 continue;
40514             }
40515             reg.showPanel(reg.getPanel(0));
40516         }
40517         
40518         
40519         
40520         
40521     },
40522     
40523     /**
40524      * Returns the nested BorderLayout for this panel
40525      * @return {Roo.BorderLayout} 
40526      */
40527     getLayout : function(){
40528         return this.layout;
40529     },
40530     
40531      /**
40532      * Adds a xtype elements to the layout of the nested panel
40533      * <pre><code>
40534
40535 panel.addxtype({
40536        xtype : 'ContentPanel',
40537        region: 'west',
40538        items: [ .... ]
40539    }
40540 );
40541
40542 panel.addxtype({
40543         xtype : 'NestedLayoutPanel',
40544         region: 'west',
40545         layout: {
40546            center: { },
40547            west: { }   
40548         },
40549         items : [ ... list of content panels or nested layout panels.. ]
40550    }
40551 );
40552 </code></pre>
40553      * @param {Object} cfg Xtype definition of item to add.
40554      */
40555     addxtype : function(cfg) {
40556         return this.layout.addxtype(cfg);
40557     
40558     }
40559 });/*
40560  * Based on:
40561  * Ext JS Library 1.1.1
40562  * Copyright(c) 2006-2007, Ext JS, LLC.
40563  *
40564  * Originally Released Under LGPL - original licence link has changed is not relivant.
40565  *
40566  * Fork - LGPL
40567  * <script type="text/javascript">
40568  */
40569 /**
40570  * @class Roo.TabPanel
40571  * @extends Roo.util.Observable
40572  * A lightweight tab container.
40573  * <br><br>
40574  * Usage:
40575  * <pre><code>
40576 // basic tabs 1, built from existing content
40577 var tabs = new Roo.TabPanel("tabs1");
40578 tabs.addTab("script", "View Script");
40579 tabs.addTab("markup", "View Markup");
40580 tabs.activate("script");
40581
40582 // more advanced tabs, built from javascript
40583 var jtabs = new Roo.TabPanel("jtabs");
40584 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40585
40586 // set up the UpdateManager
40587 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40588 var updater = tab2.getUpdateManager();
40589 updater.setDefaultUrl("ajax1.htm");
40590 tab2.on('activate', updater.refresh, updater, true);
40591
40592 // Use setUrl for Ajax loading
40593 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40594 tab3.setUrl("ajax2.htm", null, true);
40595
40596 // Disabled tab
40597 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40598 tab4.disable();
40599
40600 jtabs.activate("jtabs-1");
40601  * </code></pre>
40602  * @constructor
40603  * Create a new TabPanel.
40604  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40605  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40606  */
40607 Roo.bootstrap.panel.Tabs = function(config){
40608     /**
40609     * The container element for this TabPanel.
40610     * @type Roo.Element
40611     */
40612     this.el = Roo.get(config.el);
40613     delete config.el;
40614     if(config){
40615         if(typeof config == "boolean"){
40616             this.tabPosition = config ? "bottom" : "top";
40617         }else{
40618             Roo.apply(this, config);
40619         }
40620     }
40621     
40622     if(this.tabPosition == "bottom"){
40623         // if tabs are at the bottom = create the body first.
40624         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40625         this.el.addClass("roo-tabs-bottom");
40626     }
40627     // next create the tabs holders
40628     
40629     if (this.tabPosition == "west"){
40630         
40631         var reg = this.region; // fake it..
40632         while (reg) {
40633             if (!reg.mgr.parent) {
40634                 break;
40635             }
40636             reg = reg.mgr.parent.region;
40637         }
40638         Roo.log("got nest?");
40639         Roo.log(reg);
40640         if (reg.mgr.getRegion('west')) {
40641             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40642             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40643             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40644             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40645             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40646         
40647             
40648         }
40649         
40650         
40651     } else {
40652      
40653         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40654         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40655         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40656         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40657     }
40658     
40659     
40660     if(Roo.isIE){
40661         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40662     }
40663     
40664     // finally - if tabs are at the top, then create the body last..
40665     if(this.tabPosition != "bottom"){
40666         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40667          * @type Roo.Element
40668          */
40669         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40670         this.el.addClass("roo-tabs-top");
40671     }
40672     this.items = [];
40673
40674     this.bodyEl.setStyle("position", "relative");
40675
40676     this.active = null;
40677     this.activateDelegate = this.activate.createDelegate(this);
40678
40679     this.addEvents({
40680         /**
40681          * @event tabchange
40682          * Fires when the active tab changes
40683          * @param {Roo.TabPanel} this
40684          * @param {Roo.TabPanelItem} activePanel The new active tab
40685          */
40686         "tabchange": true,
40687         /**
40688          * @event beforetabchange
40689          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40690          * @param {Roo.TabPanel} this
40691          * @param {Object} e Set cancel to true on this object to cancel the tab change
40692          * @param {Roo.TabPanelItem} tab The tab being changed to
40693          */
40694         "beforetabchange" : true
40695     });
40696
40697     Roo.EventManager.onWindowResize(this.onResize, this);
40698     this.cpad = this.el.getPadding("lr");
40699     this.hiddenCount = 0;
40700
40701
40702     // toolbar on the tabbar support...
40703     if (this.toolbar) {
40704         alert("no toolbar support yet");
40705         this.toolbar  = false;
40706         /*
40707         var tcfg = this.toolbar;
40708         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40709         this.toolbar = new Roo.Toolbar(tcfg);
40710         if (Roo.isSafari) {
40711             var tbl = tcfg.container.child('table', true);
40712             tbl.setAttribute('width', '100%');
40713         }
40714         */
40715         
40716     }
40717    
40718
40719
40720     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40721 };
40722
40723 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40724     /*
40725      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40726      */
40727     tabPosition : "top",
40728     /*
40729      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40730      */
40731     currentTabWidth : 0,
40732     /*
40733      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40734      */
40735     minTabWidth : 40,
40736     /*
40737      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40738      */
40739     maxTabWidth : 250,
40740     /*
40741      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40742      */
40743     preferredTabWidth : 175,
40744     /*
40745      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40746      */
40747     resizeTabs : false,
40748     /*
40749      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40750      */
40751     monitorResize : true,
40752     /*
40753      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40754      */
40755     toolbar : false,  // set by caller..
40756     
40757     region : false, /// set by caller
40758     
40759     disableTooltips : true, // not used yet...
40760
40761     /**
40762      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40763      * @param {String} id The id of the div to use <b>or create</b>
40764      * @param {String} text The text for the tab
40765      * @param {String} content (optional) Content to put in the TabPanelItem body
40766      * @param {Boolean} closable (optional) True to create a close icon on the tab
40767      * @return {Roo.TabPanelItem} The created TabPanelItem
40768      */
40769     addTab : function(id, text, content, closable, tpl)
40770     {
40771         var item = new Roo.bootstrap.panel.TabItem({
40772             panel: this,
40773             id : id,
40774             text : text,
40775             closable : closable,
40776             tpl : tpl
40777         });
40778         this.addTabItem(item);
40779         if(content){
40780             item.setContent(content);
40781         }
40782         return item;
40783     },
40784
40785     /**
40786      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40787      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40788      * @return {Roo.TabPanelItem}
40789      */
40790     getTab : function(id){
40791         return this.items[id];
40792     },
40793
40794     /**
40795      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40796      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40797      */
40798     hideTab : function(id){
40799         var t = this.items[id];
40800         if(!t.isHidden()){
40801            t.setHidden(true);
40802            this.hiddenCount++;
40803            this.autoSizeTabs();
40804         }
40805     },
40806
40807     /**
40808      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40809      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40810      */
40811     unhideTab : function(id){
40812         var t = this.items[id];
40813         if(t.isHidden()){
40814            t.setHidden(false);
40815            this.hiddenCount--;
40816            this.autoSizeTabs();
40817         }
40818     },
40819
40820     /**
40821      * Adds an existing {@link Roo.TabPanelItem}.
40822      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40823      */
40824     addTabItem : function(item)
40825     {
40826         this.items[item.id] = item;
40827         this.items.push(item);
40828         this.autoSizeTabs();
40829       //  if(this.resizeTabs){
40830     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40831   //         this.autoSizeTabs();
40832 //        }else{
40833 //            item.autoSize();
40834        // }
40835     },
40836
40837     /**
40838      * Removes a {@link Roo.TabPanelItem}.
40839      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40840      */
40841     removeTab : function(id){
40842         var items = this.items;
40843         var tab = items[id];
40844         if(!tab) { return; }
40845         var index = items.indexOf(tab);
40846         if(this.active == tab && items.length > 1){
40847             var newTab = this.getNextAvailable(index);
40848             if(newTab) {
40849                 newTab.activate();
40850             }
40851         }
40852         this.stripEl.dom.removeChild(tab.pnode.dom);
40853         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40854             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40855         }
40856         items.splice(index, 1);
40857         delete this.items[tab.id];
40858         tab.fireEvent("close", tab);
40859         tab.purgeListeners();
40860         this.autoSizeTabs();
40861     },
40862
40863     getNextAvailable : function(start){
40864         var items = this.items;
40865         var index = start;
40866         // look for a next tab that will slide over to
40867         // replace the one being removed
40868         while(index < items.length){
40869             var item = items[++index];
40870             if(item && !item.isHidden()){
40871                 return item;
40872             }
40873         }
40874         // if one isn't found select the previous tab (on the left)
40875         index = start;
40876         while(index >= 0){
40877             var item = items[--index];
40878             if(item && !item.isHidden()){
40879                 return item;
40880             }
40881         }
40882         return null;
40883     },
40884
40885     /**
40886      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40887      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40888      */
40889     disableTab : function(id){
40890         var tab = this.items[id];
40891         if(tab && this.active != tab){
40892             tab.disable();
40893         }
40894     },
40895
40896     /**
40897      * Enables a {@link Roo.TabPanelItem} that is disabled.
40898      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40899      */
40900     enableTab : function(id){
40901         var tab = this.items[id];
40902         tab.enable();
40903     },
40904
40905     /**
40906      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40907      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40908      * @return {Roo.TabPanelItem} The TabPanelItem.
40909      */
40910     activate : function(id)
40911     {
40912         //Roo.log('activite:'  + id);
40913         
40914         var tab = this.items[id];
40915         if(!tab){
40916             return null;
40917         }
40918         if(tab == this.active || tab.disabled){
40919             return tab;
40920         }
40921         var e = {};
40922         this.fireEvent("beforetabchange", this, e, tab);
40923         if(e.cancel !== true && !tab.disabled){
40924             if(this.active){
40925                 this.active.hide();
40926             }
40927             this.active = this.items[id];
40928             this.active.show();
40929             this.fireEvent("tabchange", this, this.active);
40930         }
40931         return tab;
40932     },
40933
40934     /**
40935      * Gets the active {@link Roo.TabPanelItem}.
40936      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40937      */
40938     getActiveTab : function(){
40939         return this.active;
40940     },
40941
40942     /**
40943      * Updates the tab body element to fit the height of the container element
40944      * for overflow scrolling
40945      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40946      */
40947     syncHeight : function(targetHeight){
40948         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40949         var bm = this.bodyEl.getMargins();
40950         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40951         this.bodyEl.setHeight(newHeight);
40952         return newHeight;
40953     },
40954
40955     onResize : function(){
40956         if(this.monitorResize){
40957             this.autoSizeTabs();
40958         }
40959     },
40960
40961     /**
40962      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40963      */
40964     beginUpdate : function(){
40965         this.updating = true;
40966     },
40967
40968     /**
40969      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40970      */
40971     endUpdate : function(){
40972         this.updating = false;
40973         this.autoSizeTabs();
40974     },
40975
40976     /**
40977      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40978      */
40979     autoSizeTabs : function()
40980     {
40981         var count = this.items.length;
40982         var vcount = count - this.hiddenCount;
40983         
40984         if (vcount < 2) {
40985             this.stripEl.hide();
40986         } else {
40987             this.stripEl.show();
40988         }
40989         
40990         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40991             return;
40992         }
40993         
40994         
40995         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40996         var availWidth = Math.floor(w / vcount);
40997         var b = this.stripBody;
40998         if(b.getWidth() > w){
40999             var tabs = this.items;
41000             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41001             if(availWidth < this.minTabWidth){
41002                 /*if(!this.sleft){    // incomplete scrolling code
41003                     this.createScrollButtons();
41004                 }
41005                 this.showScroll();
41006                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41007             }
41008         }else{
41009             if(this.currentTabWidth < this.preferredTabWidth){
41010                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41011             }
41012         }
41013     },
41014
41015     /**
41016      * Returns the number of tabs in this TabPanel.
41017      * @return {Number}
41018      */
41019      getCount : function(){
41020          return this.items.length;
41021      },
41022
41023     /**
41024      * Resizes all the tabs to the passed width
41025      * @param {Number} The new width
41026      */
41027     setTabWidth : function(width){
41028         this.currentTabWidth = width;
41029         for(var i = 0, len = this.items.length; i < len; i++) {
41030                 if(!this.items[i].isHidden()) {
41031                 this.items[i].setWidth(width);
41032             }
41033         }
41034     },
41035
41036     /**
41037      * Destroys this TabPanel
41038      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41039      */
41040     destroy : function(removeEl){
41041         Roo.EventManager.removeResizeListener(this.onResize, this);
41042         for(var i = 0, len = this.items.length; i < len; i++){
41043             this.items[i].purgeListeners();
41044         }
41045         if(removeEl === true){
41046             this.el.update("");
41047             this.el.remove();
41048         }
41049     },
41050     
41051     createStrip : function(container)
41052     {
41053         var strip = document.createElement("nav");
41054         strip.className = Roo.bootstrap.version == 4 ?
41055             "navbar-light bg-light" : 
41056             "navbar navbar-default"; //"x-tabs-wrap";
41057         container.appendChild(strip);
41058         return strip;
41059     },
41060     
41061     createStripList : function(strip)
41062     {
41063         // div wrapper for retard IE
41064         // returns the "tr" element.
41065         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41066         //'<div class="x-tabs-strip-wrap">'+
41067           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41068           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41069         return strip.firstChild; //.firstChild.firstChild.firstChild;
41070     },
41071     createBody : function(container)
41072     {
41073         var body = document.createElement("div");
41074         Roo.id(body, "tab-body");
41075         //Roo.fly(body).addClass("x-tabs-body");
41076         Roo.fly(body).addClass("tab-content");
41077         container.appendChild(body);
41078         return body;
41079     },
41080     createItemBody :function(bodyEl, id){
41081         var body = Roo.getDom(id);
41082         if(!body){
41083             body = document.createElement("div");
41084             body.id = id;
41085         }
41086         //Roo.fly(body).addClass("x-tabs-item-body");
41087         Roo.fly(body).addClass("tab-pane");
41088          bodyEl.insertBefore(body, bodyEl.firstChild);
41089         return body;
41090     },
41091     /** @private */
41092     createStripElements :  function(stripEl, text, closable, tpl)
41093     {
41094         var td = document.createElement("li"); // was td..
41095         td.className = 'nav-item';
41096         
41097         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41098         
41099         
41100         stripEl.appendChild(td);
41101         /*if(closable){
41102             td.className = "x-tabs-closable";
41103             if(!this.closeTpl){
41104                 this.closeTpl = new Roo.Template(
41105                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41106                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41107                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41108                 );
41109             }
41110             var el = this.closeTpl.overwrite(td, {"text": text});
41111             var close = el.getElementsByTagName("div")[0];
41112             var inner = el.getElementsByTagName("em")[0];
41113             return {"el": el, "close": close, "inner": inner};
41114         } else {
41115         */
41116         // not sure what this is..
41117 //            if(!this.tabTpl){
41118                 //this.tabTpl = new Roo.Template(
41119                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41120                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41121                 //);
41122 //                this.tabTpl = new Roo.Template(
41123 //                   '<a href="#">' +
41124 //                   '<span unselectable="on"' +
41125 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41126 //                            ' >{text}</span></a>'
41127 //                );
41128 //                
41129 //            }
41130
41131
41132             var template = tpl || this.tabTpl || false;
41133             
41134             if(!template){
41135                 template =  new Roo.Template(
41136                         Roo.bootstrap.version == 4 ? 
41137                             (
41138                                 '<a class="nav-link" href="#" unselectable="on"' +
41139                                      (this.disableTooltips ? '' : ' title="{text}"') +
41140                                      ' >{text}</a>'
41141                             ) : (
41142                                 '<a class="nav-link" href="#">' +
41143                                 '<span unselectable="on"' +
41144                                          (this.disableTooltips ? '' : ' title="{text}"') +
41145                                     ' >{text}</span></a>'
41146                             )
41147                 );
41148             }
41149             
41150             switch (typeof(template)) {
41151                 case 'object' :
41152                     break;
41153                 case 'string' :
41154                     template = new Roo.Template(template);
41155                     break;
41156                 default :
41157                     break;
41158             }
41159             
41160             var el = template.overwrite(td, {"text": text});
41161             
41162             var inner = el.getElementsByTagName("span")[0];
41163             
41164             return {"el": el, "inner": inner};
41165             
41166     }
41167         
41168     
41169 });
41170
41171 /**
41172  * @class Roo.TabPanelItem
41173  * @extends Roo.util.Observable
41174  * Represents an individual item (tab plus body) in a TabPanel.
41175  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41176  * @param {String} id The id of this TabPanelItem
41177  * @param {String} text The text for the tab of this TabPanelItem
41178  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41179  */
41180 Roo.bootstrap.panel.TabItem = function(config){
41181     /**
41182      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41183      * @type Roo.TabPanel
41184      */
41185     this.tabPanel = config.panel;
41186     /**
41187      * The id for this TabPanelItem
41188      * @type String
41189      */
41190     this.id = config.id;
41191     /** @private */
41192     this.disabled = false;
41193     /** @private */
41194     this.text = config.text;
41195     /** @private */
41196     this.loaded = false;
41197     this.closable = config.closable;
41198
41199     /**
41200      * The body element for this TabPanelItem.
41201      * @type Roo.Element
41202      */
41203     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41204     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41205     this.bodyEl.setStyle("display", "block");
41206     this.bodyEl.setStyle("zoom", "1");
41207     //this.hideAction();
41208
41209     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41210     /** @private */
41211     this.el = Roo.get(els.el);
41212     this.inner = Roo.get(els.inner, true);
41213      this.textEl = Roo.bootstrap.version == 4 ?
41214         this.el : Roo.get(this.el.dom.firstChild, true);
41215
41216     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41217     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41218
41219     
41220 //    this.el.on("mousedown", this.onTabMouseDown, this);
41221     this.el.on("click", this.onTabClick, this);
41222     /** @private */
41223     if(config.closable){
41224         var c = Roo.get(els.close, true);
41225         c.dom.title = this.closeText;
41226         c.addClassOnOver("close-over");
41227         c.on("click", this.closeClick, this);
41228      }
41229
41230     this.addEvents({
41231          /**
41232          * @event activate
41233          * Fires when this tab becomes the active tab.
41234          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41235          * @param {Roo.TabPanelItem} this
41236          */
41237         "activate": true,
41238         /**
41239          * @event beforeclose
41240          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41241          * @param {Roo.TabPanelItem} this
41242          * @param {Object} e Set cancel to true on this object to cancel the close.
41243          */
41244         "beforeclose": true,
41245         /**
41246          * @event close
41247          * Fires when this tab is closed.
41248          * @param {Roo.TabPanelItem} this
41249          */
41250          "close": true,
41251         /**
41252          * @event deactivate
41253          * Fires when this tab is no longer the active tab.
41254          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41255          * @param {Roo.TabPanelItem} this
41256          */
41257          "deactivate" : true
41258     });
41259     this.hidden = false;
41260
41261     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41262 };
41263
41264 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41265            {
41266     purgeListeners : function(){
41267        Roo.util.Observable.prototype.purgeListeners.call(this);
41268        this.el.removeAllListeners();
41269     },
41270     /**
41271      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41272      */
41273     show : function(){
41274         this.status_node.addClass("active");
41275         this.showAction();
41276         if(Roo.isOpera){
41277             this.tabPanel.stripWrap.repaint();
41278         }
41279         this.fireEvent("activate", this.tabPanel, this);
41280     },
41281
41282     /**
41283      * Returns true if this tab is the active tab.
41284      * @return {Boolean}
41285      */
41286     isActive : function(){
41287         return this.tabPanel.getActiveTab() == this;
41288     },
41289
41290     /**
41291      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41292      */
41293     hide : function(){
41294         this.status_node.removeClass("active");
41295         this.hideAction();
41296         this.fireEvent("deactivate", this.tabPanel, this);
41297     },
41298
41299     hideAction : function(){
41300         this.bodyEl.hide();
41301         this.bodyEl.setStyle("position", "absolute");
41302         this.bodyEl.setLeft("-20000px");
41303         this.bodyEl.setTop("-20000px");
41304     },
41305
41306     showAction : function(){
41307         this.bodyEl.setStyle("position", "relative");
41308         this.bodyEl.setTop("");
41309         this.bodyEl.setLeft("");
41310         this.bodyEl.show();
41311     },
41312
41313     /**
41314      * Set the tooltip for the tab.
41315      * @param {String} tooltip The tab's tooltip
41316      */
41317     setTooltip : function(text){
41318         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41319             this.textEl.dom.qtip = text;
41320             this.textEl.dom.removeAttribute('title');
41321         }else{
41322             this.textEl.dom.title = text;
41323         }
41324     },
41325
41326     onTabClick : function(e){
41327         e.preventDefault();
41328         this.tabPanel.activate(this.id);
41329     },
41330
41331     onTabMouseDown : function(e){
41332         e.preventDefault();
41333         this.tabPanel.activate(this.id);
41334     },
41335 /*
41336     getWidth : function(){
41337         return this.inner.getWidth();
41338     },
41339
41340     setWidth : function(width){
41341         var iwidth = width - this.linode.getPadding("lr");
41342         this.inner.setWidth(iwidth);
41343         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41344         this.linode.setWidth(width);
41345     },
41346 */
41347     /**
41348      * Show or hide the tab
41349      * @param {Boolean} hidden True to hide or false to show.
41350      */
41351     setHidden : function(hidden){
41352         this.hidden = hidden;
41353         this.linode.setStyle("display", hidden ? "none" : "");
41354     },
41355
41356     /**
41357      * Returns true if this tab is "hidden"
41358      * @return {Boolean}
41359      */
41360     isHidden : function(){
41361         return this.hidden;
41362     },
41363
41364     /**
41365      * Returns the text for this tab
41366      * @return {String}
41367      */
41368     getText : function(){
41369         return this.text;
41370     },
41371     /*
41372     autoSize : function(){
41373         //this.el.beginMeasure();
41374         this.textEl.setWidth(1);
41375         /*
41376          *  #2804 [new] Tabs in Roojs
41377          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41378          */
41379         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41380         //this.el.endMeasure();
41381     //},
41382
41383     /**
41384      * Sets the text for the tab (Note: this also sets the tooltip text)
41385      * @param {String} text The tab's text and tooltip
41386      */
41387     setText : function(text){
41388         this.text = text;
41389         this.textEl.update(text);
41390         this.setTooltip(text);
41391         //if(!this.tabPanel.resizeTabs){
41392         //    this.autoSize();
41393         //}
41394     },
41395     /**
41396      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41397      */
41398     activate : function(){
41399         this.tabPanel.activate(this.id);
41400     },
41401
41402     /**
41403      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41404      */
41405     disable : function(){
41406         if(this.tabPanel.active != this){
41407             this.disabled = true;
41408             this.status_node.addClass("disabled");
41409         }
41410     },
41411
41412     /**
41413      * Enables this TabPanelItem if it was previously disabled.
41414      */
41415     enable : function(){
41416         this.disabled = false;
41417         this.status_node.removeClass("disabled");
41418     },
41419
41420     /**
41421      * Sets the content for this TabPanelItem.
41422      * @param {String} content The content
41423      * @param {Boolean} loadScripts true to look for and load scripts
41424      */
41425     setContent : function(content, loadScripts){
41426         this.bodyEl.update(content, loadScripts);
41427     },
41428
41429     /**
41430      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41431      * @return {Roo.UpdateManager} The UpdateManager
41432      */
41433     getUpdateManager : function(){
41434         return this.bodyEl.getUpdateManager();
41435     },
41436
41437     /**
41438      * Set a URL to be used to load the content for this TabPanelItem.
41439      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41440      * @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)
41441      * @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)
41442      * @return {Roo.UpdateManager} The UpdateManager
41443      */
41444     setUrl : function(url, params, loadOnce){
41445         if(this.refreshDelegate){
41446             this.un('activate', this.refreshDelegate);
41447         }
41448         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41449         this.on("activate", this.refreshDelegate);
41450         return this.bodyEl.getUpdateManager();
41451     },
41452
41453     /** @private */
41454     _handleRefresh : function(url, params, loadOnce){
41455         if(!loadOnce || !this.loaded){
41456             var updater = this.bodyEl.getUpdateManager();
41457             updater.update(url, params, this._setLoaded.createDelegate(this));
41458         }
41459     },
41460
41461     /**
41462      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41463      *   Will fail silently if the setUrl method has not been called.
41464      *   This does not activate the panel, just updates its content.
41465      */
41466     refresh : function(){
41467         if(this.refreshDelegate){
41468            this.loaded = false;
41469            this.refreshDelegate();
41470         }
41471     },
41472
41473     /** @private */
41474     _setLoaded : function(){
41475         this.loaded = true;
41476     },
41477
41478     /** @private */
41479     closeClick : function(e){
41480         var o = {};
41481         e.stopEvent();
41482         this.fireEvent("beforeclose", this, o);
41483         if(o.cancel !== true){
41484             this.tabPanel.removeTab(this.id);
41485         }
41486     },
41487     /**
41488      * The text displayed in the tooltip for the close icon.
41489      * @type String
41490      */
41491     closeText : "Close this tab"
41492 });
41493 /**
41494 *    This script refer to:
41495 *    Title: International Telephone Input
41496 *    Author: Jack O'Connor
41497 *    Code version:  v12.1.12
41498 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41499 **/
41500
41501 Roo.bootstrap.PhoneInputData = function() {
41502     var d = [
41503       [
41504         "Afghanistan (‫افغانستان‬‎)",
41505         "af",
41506         "93"
41507       ],
41508       [
41509         "Albania (Shqipëri)",
41510         "al",
41511         "355"
41512       ],
41513       [
41514         "Algeria (‫الجزائر‬‎)",
41515         "dz",
41516         "213"
41517       ],
41518       [
41519         "American Samoa",
41520         "as",
41521         "1684"
41522       ],
41523       [
41524         "Andorra",
41525         "ad",
41526         "376"
41527       ],
41528       [
41529         "Angola",
41530         "ao",
41531         "244"
41532       ],
41533       [
41534         "Anguilla",
41535         "ai",
41536         "1264"
41537       ],
41538       [
41539         "Antigua and Barbuda",
41540         "ag",
41541         "1268"
41542       ],
41543       [
41544         "Argentina",
41545         "ar",
41546         "54"
41547       ],
41548       [
41549         "Armenia (Հայաստան)",
41550         "am",
41551         "374"
41552       ],
41553       [
41554         "Aruba",
41555         "aw",
41556         "297"
41557       ],
41558       [
41559         "Australia",
41560         "au",
41561         "61",
41562         0
41563       ],
41564       [
41565         "Austria (Österreich)",
41566         "at",
41567         "43"
41568       ],
41569       [
41570         "Azerbaijan (Azərbaycan)",
41571         "az",
41572         "994"
41573       ],
41574       [
41575         "Bahamas",
41576         "bs",
41577         "1242"
41578       ],
41579       [
41580         "Bahrain (‫البحرين‬‎)",
41581         "bh",
41582         "973"
41583       ],
41584       [
41585         "Bangladesh (বাংলাদেশ)",
41586         "bd",
41587         "880"
41588       ],
41589       [
41590         "Barbados",
41591         "bb",
41592         "1246"
41593       ],
41594       [
41595         "Belarus (Беларусь)",
41596         "by",
41597         "375"
41598       ],
41599       [
41600         "Belgium (België)",
41601         "be",
41602         "32"
41603       ],
41604       [
41605         "Belize",
41606         "bz",
41607         "501"
41608       ],
41609       [
41610         "Benin (Bénin)",
41611         "bj",
41612         "229"
41613       ],
41614       [
41615         "Bermuda",
41616         "bm",
41617         "1441"
41618       ],
41619       [
41620         "Bhutan (འབྲུག)",
41621         "bt",
41622         "975"
41623       ],
41624       [
41625         "Bolivia",
41626         "bo",
41627         "591"
41628       ],
41629       [
41630         "Bosnia and Herzegovina (Босна и Херцеговина)",
41631         "ba",
41632         "387"
41633       ],
41634       [
41635         "Botswana",
41636         "bw",
41637         "267"
41638       ],
41639       [
41640         "Brazil (Brasil)",
41641         "br",
41642         "55"
41643       ],
41644       [
41645         "British Indian Ocean Territory",
41646         "io",
41647         "246"
41648       ],
41649       [
41650         "British Virgin Islands",
41651         "vg",
41652         "1284"
41653       ],
41654       [
41655         "Brunei",
41656         "bn",
41657         "673"
41658       ],
41659       [
41660         "Bulgaria (България)",
41661         "bg",
41662         "359"
41663       ],
41664       [
41665         "Burkina Faso",
41666         "bf",
41667         "226"
41668       ],
41669       [
41670         "Burundi (Uburundi)",
41671         "bi",
41672         "257"
41673       ],
41674       [
41675         "Cambodia (កម្ពុជា)",
41676         "kh",
41677         "855"
41678       ],
41679       [
41680         "Cameroon (Cameroun)",
41681         "cm",
41682         "237"
41683       ],
41684       [
41685         "Canada",
41686         "ca",
41687         "1",
41688         1,
41689         ["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"]
41690       ],
41691       [
41692         "Cape Verde (Kabu Verdi)",
41693         "cv",
41694         "238"
41695       ],
41696       [
41697         "Caribbean Netherlands",
41698         "bq",
41699         "599",
41700         1
41701       ],
41702       [
41703         "Cayman Islands",
41704         "ky",
41705         "1345"
41706       ],
41707       [
41708         "Central African Republic (République centrafricaine)",
41709         "cf",
41710         "236"
41711       ],
41712       [
41713         "Chad (Tchad)",
41714         "td",
41715         "235"
41716       ],
41717       [
41718         "Chile",
41719         "cl",
41720         "56"
41721       ],
41722       [
41723         "China (中国)",
41724         "cn",
41725         "86"
41726       ],
41727       [
41728         "Christmas Island",
41729         "cx",
41730         "61",
41731         2
41732       ],
41733       [
41734         "Cocos (Keeling) Islands",
41735         "cc",
41736         "61",
41737         1
41738       ],
41739       [
41740         "Colombia",
41741         "co",
41742         "57"
41743       ],
41744       [
41745         "Comoros (‫جزر القمر‬‎)",
41746         "km",
41747         "269"
41748       ],
41749       [
41750         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41751         "cd",
41752         "243"
41753       ],
41754       [
41755         "Congo (Republic) (Congo-Brazzaville)",
41756         "cg",
41757         "242"
41758       ],
41759       [
41760         "Cook Islands",
41761         "ck",
41762         "682"
41763       ],
41764       [
41765         "Costa Rica",
41766         "cr",
41767         "506"
41768       ],
41769       [
41770         "Côte d’Ivoire",
41771         "ci",
41772         "225"
41773       ],
41774       [
41775         "Croatia (Hrvatska)",
41776         "hr",
41777         "385"
41778       ],
41779       [
41780         "Cuba",
41781         "cu",
41782         "53"
41783       ],
41784       [
41785         "Curaçao",
41786         "cw",
41787         "599",
41788         0
41789       ],
41790       [
41791         "Cyprus (Κύπρος)",
41792         "cy",
41793         "357"
41794       ],
41795       [
41796         "Czech Republic (Česká republika)",
41797         "cz",
41798         "420"
41799       ],
41800       [
41801         "Denmark (Danmark)",
41802         "dk",
41803         "45"
41804       ],
41805       [
41806         "Djibouti",
41807         "dj",
41808         "253"
41809       ],
41810       [
41811         "Dominica",
41812         "dm",
41813         "1767"
41814       ],
41815       [
41816         "Dominican Republic (República Dominicana)",
41817         "do",
41818         "1",
41819         2,
41820         ["809", "829", "849"]
41821       ],
41822       [
41823         "Ecuador",
41824         "ec",
41825         "593"
41826       ],
41827       [
41828         "Egypt (‫مصر‬‎)",
41829         "eg",
41830         "20"
41831       ],
41832       [
41833         "El Salvador",
41834         "sv",
41835         "503"
41836       ],
41837       [
41838         "Equatorial Guinea (Guinea Ecuatorial)",
41839         "gq",
41840         "240"
41841       ],
41842       [
41843         "Eritrea",
41844         "er",
41845         "291"
41846       ],
41847       [
41848         "Estonia (Eesti)",
41849         "ee",
41850         "372"
41851       ],
41852       [
41853         "Ethiopia",
41854         "et",
41855         "251"
41856       ],
41857       [
41858         "Falkland Islands (Islas Malvinas)",
41859         "fk",
41860         "500"
41861       ],
41862       [
41863         "Faroe Islands (Føroyar)",
41864         "fo",
41865         "298"
41866       ],
41867       [
41868         "Fiji",
41869         "fj",
41870         "679"
41871       ],
41872       [
41873         "Finland (Suomi)",
41874         "fi",
41875         "358",
41876         0
41877       ],
41878       [
41879         "France",
41880         "fr",
41881         "33"
41882       ],
41883       [
41884         "French Guiana (Guyane française)",
41885         "gf",
41886         "594"
41887       ],
41888       [
41889         "French Polynesia (Polynésie française)",
41890         "pf",
41891         "689"
41892       ],
41893       [
41894         "Gabon",
41895         "ga",
41896         "241"
41897       ],
41898       [
41899         "Gambia",
41900         "gm",
41901         "220"
41902       ],
41903       [
41904         "Georgia (საქართველო)",
41905         "ge",
41906         "995"
41907       ],
41908       [
41909         "Germany (Deutschland)",
41910         "de",
41911         "49"
41912       ],
41913       [
41914         "Ghana (Gaana)",
41915         "gh",
41916         "233"
41917       ],
41918       [
41919         "Gibraltar",
41920         "gi",
41921         "350"
41922       ],
41923       [
41924         "Greece (Ελλάδα)",
41925         "gr",
41926         "30"
41927       ],
41928       [
41929         "Greenland (Kalaallit Nunaat)",
41930         "gl",
41931         "299"
41932       ],
41933       [
41934         "Grenada",
41935         "gd",
41936         "1473"
41937       ],
41938       [
41939         "Guadeloupe",
41940         "gp",
41941         "590",
41942         0
41943       ],
41944       [
41945         "Guam",
41946         "gu",
41947         "1671"
41948       ],
41949       [
41950         "Guatemala",
41951         "gt",
41952         "502"
41953       ],
41954       [
41955         "Guernsey",
41956         "gg",
41957         "44",
41958         1
41959       ],
41960       [
41961         "Guinea (Guinée)",
41962         "gn",
41963         "224"
41964       ],
41965       [
41966         "Guinea-Bissau (Guiné Bissau)",
41967         "gw",
41968         "245"
41969       ],
41970       [
41971         "Guyana",
41972         "gy",
41973         "592"
41974       ],
41975       [
41976         "Haiti",
41977         "ht",
41978         "509"
41979       ],
41980       [
41981         "Honduras",
41982         "hn",
41983         "504"
41984       ],
41985       [
41986         "Hong Kong (香港)",
41987         "hk",
41988         "852"
41989       ],
41990       [
41991         "Hungary (Magyarország)",
41992         "hu",
41993         "36"
41994       ],
41995       [
41996         "Iceland (Ísland)",
41997         "is",
41998         "354"
41999       ],
42000       [
42001         "India (भारत)",
42002         "in",
42003         "91"
42004       ],
42005       [
42006         "Indonesia",
42007         "id",
42008         "62"
42009       ],
42010       [
42011         "Iran (‫ایران‬‎)",
42012         "ir",
42013         "98"
42014       ],
42015       [
42016         "Iraq (‫العراق‬‎)",
42017         "iq",
42018         "964"
42019       ],
42020       [
42021         "Ireland",
42022         "ie",
42023         "353"
42024       ],
42025       [
42026         "Isle of Man",
42027         "im",
42028         "44",
42029         2
42030       ],
42031       [
42032         "Israel (‫ישראל‬‎)",
42033         "il",
42034         "972"
42035       ],
42036       [
42037         "Italy (Italia)",
42038         "it",
42039         "39",
42040         0
42041       ],
42042       [
42043         "Jamaica",
42044         "jm",
42045         "1876"
42046       ],
42047       [
42048         "Japan (日本)",
42049         "jp",
42050         "81"
42051       ],
42052       [
42053         "Jersey",
42054         "je",
42055         "44",
42056         3
42057       ],
42058       [
42059         "Jordan (‫الأردن‬‎)",
42060         "jo",
42061         "962"
42062       ],
42063       [
42064         "Kazakhstan (Казахстан)",
42065         "kz",
42066         "7",
42067         1
42068       ],
42069       [
42070         "Kenya",
42071         "ke",
42072         "254"
42073       ],
42074       [
42075         "Kiribati",
42076         "ki",
42077         "686"
42078       ],
42079       [
42080         "Kosovo",
42081         "xk",
42082         "383"
42083       ],
42084       [
42085         "Kuwait (‫الكويت‬‎)",
42086         "kw",
42087         "965"
42088       ],
42089       [
42090         "Kyrgyzstan (Кыргызстан)",
42091         "kg",
42092         "996"
42093       ],
42094       [
42095         "Laos (ລາວ)",
42096         "la",
42097         "856"
42098       ],
42099       [
42100         "Latvia (Latvija)",
42101         "lv",
42102         "371"
42103       ],
42104       [
42105         "Lebanon (‫لبنان‬‎)",
42106         "lb",
42107         "961"
42108       ],
42109       [
42110         "Lesotho",
42111         "ls",
42112         "266"
42113       ],
42114       [
42115         "Liberia",
42116         "lr",
42117         "231"
42118       ],
42119       [
42120         "Libya (‫ليبيا‬‎)",
42121         "ly",
42122         "218"
42123       ],
42124       [
42125         "Liechtenstein",
42126         "li",
42127         "423"
42128       ],
42129       [
42130         "Lithuania (Lietuva)",
42131         "lt",
42132         "370"
42133       ],
42134       [
42135         "Luxembourg",
42136         "lu",
42137         "352"
42138       ],
42139       [
42140         "Macau (澳門)",
42141         "mo",
42142         "853"
42143       ],
42144       [
42145         "Macedonia (FYROM) (Македонија)",
42146         "mk",
42147         "389"
42148       ],
42149       [
42150         "Madagascar (Madagasikara)",
42151         "mg",
42152         "261"
42153       ],
42154       [
42155         "Malawi",
42156         "mw",
42157         "265"
42158       ],
42159       [
42160         "Malaysia",
42161         "my",
42162         "60"
42163       ],
42164       [
42165         "Maldives",
42166         "mv",
42167         "960"
42168       ],
42169       [
42170         "Mali",
42171         "ml",
42172         "223"
42173       ],
42174       [
42175         "Malta",
42176         "mt",
42177         "356"
42178       ],
42179       [
42180         "Marshall Islands",
42181         "mh",
42182         "692"
42183       ],
42184       [
42185         "Martinique",
42186         "mq",
42187         "596"
42188       ],
42189       [
42190         "Mauritania (‫موريتانيا‬‎)",
42191         "mr",
42192         "222"
42193       ],
42194       [
42195         "Mauritius (Moris)",
42196         "mu",
42197         "230"
42198       ],
42199       [
42200         "Mayotte",
42201         "yt",
42202         "262",
42203         1
42204       ],
42205       [
42206         "Mexico (México)",
42207         "mx",
42208         "52"
42209       ],
42210       [
42211         "Micronesia",
42212         "fm",
42213         "691"
42214       ],
42215       [
42216         "Moldova (Republica Moldova)",
42217         "md",
42218         "373"
42219       ],
42220       [
42221         "Monaco",
42222         "mc",
42223         "377"
42224       ],
42225       [
42226         "Mongolia (Монгол)",
42227         "mn",
42228         "976"
42229       ],
42230       [
42231         "Montenegro (Crna Gora)",
42232         "me",
42233         "382"
42234       ],
42235       [
42236         "Montserrat",
42237         "ms",
42238         "1664"
42239       ],
42240       [
42241         "Morocco (‫المغرب‬‎)",
42242         "ma",
42243         "212",
42244         0
42245       ],
42246       [
42247         "Mozambique (Moçambique)",
42248         "mz",
42249         "258"
42250       ],
42251       [
42252         "Myanmar (Burma) (မြန်မာ)",
42253         "mm",
42254         "95"
42255       ],
42256       [
42257         "Namibia (Namibië)",
42258         "na",
42259         "264"
42260       ],
42261       [
42262         "Nauru",
42263         "nr",
42264         "674"
42265       ],
42266       [
42267         "Nepal (नेपाल)",
42268         "np",
42269         "977"
42270       ],
42271       [
42272         "Netherlands (Nederland)",
42273         "nl",
42274         "31"
42275       ],
42276       [
42277         "New Caledonia (Nouvelle-Calédonie)",
42278         "nc",
42279         "687"
42280       ],
42281       [
42282         "New Zealand",
42283         "nz",
42284         "64"
42285       ],
42286       [
42287         "Nicaragua",
42288         "ni",
42289         "505"
42290       ],
42291       [
42292         "Niger (Nijar)",
42293         "ne",
42294         "227"
42295       ],
42296       [
42297         "Nigeria",
42298         "ng",
42299         "234"
42300       ],
42301       [
42302         "Niue",
42303         "nu",
42304         "683"
42305       ],
42306       [
42307         "Norfolk Island",
42308         "nf",
42309         "672"
42310       ],
42311       [
42312         "North Korea (조선 민주주의 인민 공화국)",
42313         "kp",
42314         "850"
42315       ],
42316       [
42317         "Northern Mariana Islands",
42318         "mp",
42319         "1670"
42320       ],
42321       [
42322         "Norway (Norge)",
42323         "no",
42324         "47",
42325         0
42326       ],
42327       [
42328         "Oman (‫عُمان‬‎)",
42329         "om",
42330         "968"
42331       ],
42332       [
42333         "Pakistan (‫پاکستان‬‎)",
42334         "pk",
42335         "92"
42336       ],
42337       [
42338         "Palau",
42339         "pw",
42340         "680"
42341       ],
42342       [
42343         "Palestine (‫فلسطين‬‎)",
42344         "ps",
42345         "970"
42346       ],
42347       [
42348         "Panama (Panamá)",
42349         "pa",
42350         "507"
42351       ],
42352       [
42353         "Papua New Guinea",
42354         "pg",
42355         "675"
42356       ],
42357       [
42358         "Paraguay",
42359         "py",
42360         "595"
42361       ],
42362       [
42363         "Peru (Perú)",
42364         "pe",
42365         "51"
42366       ],
42367       [
42368         "Philippines",
42369         "ph",
42370         "63"
42371       ],
42372       [
42373         "Poland (Polska)",
42374         "pl",
42375         "48"
42376       ],
42377       [
42378         "Portugal",
42379         "pt",
42380         "351"
42381       ],
42382       [
42383         "Puerto Rico",
42384         "pr",
42385         "1",
42386         3,
42387         ["787", "939"]
42388       ],
42389       [
42390         "Qatar (‫قطر‬‎)",
42391         "qa",
42392         "974"
42393       ],
42394       [
42395         "Réunion (La Réunion)",
42396         "re",
42397         "262",
42398         0
42399       ],
42400       [
42401         "Romania (România)",
42402         "ro",
42403         "40"
42404       ],
42405       [
42406         "Russia (Россия)",
42407         "ru",
42408         "7",
42409         0
42410       ],
42411       [
42412         "Rwanda",
42413         "rw",
42414         "250"
42415       ],
42416       [
42417         "Saint Barthélemy",
42418         "bl",
42419         "590",
42420         1
42421       ],
42422       [
42423         "Saint Helena",
42424         "sh",
42425         "290"
42426       ],
42427       [
42428         "Saint Kitts and Nevis",
42429         "kn",
42430         "1869"
42431       ],
42432       [
42433         "Saint Lucia",
42434         "lc",
42435         "1758"
42436       ],
42437       [
42438         "Saint Martin (Saint-Martin (partie française))",
42439         "mf",
42440         "590",
42441         2
42442       ],
42443       [
42444         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42445         "pm",
42446         "508"
42447       ],
42448       [
42449         "Saint Vincent and the Grenadines",
42450         "vc",
42451         "1784"
42452       ],
42453       [
42454         "Samoa",
42455         "ws",
42456         "685"
42457       ],
42458       [
42459         "San Marino",
42460         "sm",
42461         "378"
42462       ],
42463       [
42464         "São Tomé and Príncipe (São Tomé e Príncipe)",
42465         "st",
42466         "239"
42467       ],
42468       [
42469         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42470         "sa",
42471         "966"
42472       ],
42473       [
42474         "Senegal (Sénégal)",
42475         "sn",
42476         "221"
42477       ],
42478       [
42479         "Serbia (Србија)",
42480         "rs",
42481         "381"
42482       ],
42483       [
42484         "Seychelles",
42485         "sc",
42486         "248"
42487       ],
42488       [
42489         "Sierra Leone",
42490         "sl",
42491         "232"
42492       ],
42493       [
42494         "Singapore",
42495         "sg",
42496         "65"
42497       ],
42498       [
42499         "Sint Maarten",
42500         "sx",
42501         "1721"
42502       ],
42503       [
42504         "Slovakia (Slovensko)",
42505         "sk",
42506         "421"
42507       ],
42508       [
42509         "Slovenia (Slovenija)",
42510         "si",
42511         "386"
42512       ],
42513       [
42514         "Solomon Islands",
42515         "sb",
42516         "677"
42517       ],
42518       [
42519         "Somalia (Soomaaliya)",
42520         "so",
42521         "252"
42522       ],
42523       [
42524         "South Africa",
42525         "za",
42526         "27"
42527       ],
42528       [
42529         "South Korea (대한민국)",
42530         "kr",
42531         "82"
42532       ],
42533       [
42534         "South Sudan (‫جنوب السودان‬‎)",
42535         "ss",
42536         "211"
42537       ],
42538       [
42539         "Spain (España)",
42540         "es",
42541         "34"
42542       ],
42543       [
42544         "Sri Lanka (ශ්‍රී ලංකාව)",
42545         "lk",
42546         "94"
42547       ],
42548       [
42549         "Sudan (‫السودان‬‎)",
42550         "sd",
42551         "249"
42552       ],
42553       [
42554         "Suriname",
42555         "sr",
42556         "597"
42557       ],
42558       [
42559         "Svalbard and Jan Mayen",
42560         "sj",
42561         "47",
42562         1
42563       ],
42564       [
42565         "Swaziland",
42566         "sz",
42567         "268"
42568       ],
42569       [
42570         "Sweden (Sverige)",
42571         "se",
42572         "46"
42573       ],
42574       [
42575         "Switzerland (Schweiz)",
42576         "ch",
42577         "41"
42578       ],
42579       [
42580         "Syria (‫سوريا‬‎)",
42581         "sy",
42582         "963"
42583       ],
42584       [
42585         "Taiwan (台灣)",
42586         "tw",
42587         "886"
42588       ],
42589       [
42590         "Tajikistan",
42591         "tj",
42592         "992"
42593       ],
42594       [
42595         "Tanzania",
42596         "tz",
42597         "255"
42598       ],
42599       [
42600         "Thailand (ไทย)",
42601         "th",
42602         "66"
42603       ],
42604       [
42605         "Timor-Leste",
42606         "tl",
42607         "670"
42608       ],
42609       [
42610         "Togo",
42611         "tg",
42612         "228"
42613       ],
42614       [
42615         "Tokelau",
42616         "tk",
42617         "690"
42618       ],
42619       [
42620         "Tonga",
42621         "to",
42622         "676"
42623       ],
42624       [
42625         "Trinidad and Tobago",
42626         "tt",
42627         "1868"
42628       ],
42629       [
42630         "Tunisia (‫تونس‬‎)",
42631         "tn",
42632         "216"
42633       ],
42634       [
42635         "Turkey (Türkiye)",
42636         "tr",
42637         "90"
42638       ],
42639       [
42640         "Turkmenistan",
42641         "tm",
42642         "993"
42643       ],
42644       [
42645         "Turks and Caicos Islands",
42646         "tc",
42647         "1649"
42648       ],
42649       [
42650         "Tuvalu",
42651         "tv",
42652         "688"
42653       ],
42654       [
42655         "U.S. Virgin Islands",
42656         "vi",
42657         "1340"
42658       ],
42659       [
42660         "Uganda",
42661         "ug",
42662         "256"
42663       ],
42664       [
42665         "Ukraine (Україна)",
42666         "ua",
42667         "380"
42668       ],
42669       [
42670         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42671         "ae",
42672         "971"
42673       ],
42674       [
42675         "United Kingdom",
42676         "gb",
42677         "44",
42678         0
42679       ],
42680       [
42681         "United States",
42682         "us",
42683         "1",
42684         0
42685       ],
42686       [
42687         "Uruguay",
42688         "uy",
42689         "598"
42690       ],
42691       [
42692         "Uzbekistan (Oʻzbekiston)",
42693         "uz",
42694         "998"
42695       ],
42696       [
42697         "Vanuatu",
42698         "vu",
42699         "678"
42700       ],
42701       [
42702         "Vatican City (Città del Vaticano)",
42703         "va",
42704         "39",
42705         1
42706       ],
42707       [
42708         "Venezuela",
42709         "ve",
42710         "58"
42711       ],
42712       [
42713         "Vietnam (Việt Nam)",
42714         "vn",
42715         "84"
42716       ],
42717       [
42718         "Wallis and Futuna (Wallis-et-Futuna)",
42719         "wf",
42720         "681"
42721       ],
42722       [
42723         "Western Sahara (‫الصحراء الغربية‬‎)",
42724         "eh",
42725         "212",
42726         1
42727       ],
42728       [
42729         "Yemen (‫اليمن‬‎)",
42730         "ye",
42731         "967"
42732       ],
42733       [
42734         "Zambia",
42735         "zm",
42736         "260"
42737       ],
42738       [
42739         "Zimbabwe",
42740         "zw",
42741         "263"
42742       ],
42743       [
42744         "Åland Islands",
42745         "ax",
42746         "358",
42747         1
42748       ]
42749   ];
42750   
42751   return d;
42752 }/**
42753 *    This script refer to:
42754 *    Title: International Telephone Input
42755 *    Author: Jack O'Connor
42756 *    Code version:  v12.1.12
42757 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42758 **/
42759
42760 /**
42761  * @class Roo.bootstrap.PhoneInput
42762  * @extends Roo.bootstrap.TriggerField
42763  * An input with International dial-code selection
42764  
42765  * @cfg {String} defaultDialCode default '+852'
42766  * @cfg {Array} preferedCountries default []
42767   
42768  * @constructor
42769  * Create a new PhoneInput.
42770  * @param {Object} config Configuration options
42771  */
42772
42773 Roo.bootstrap.PhoneInput = function(config) {
42774     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42775 };
42776
42777 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42778         
42779         listWidth: undefined,
42780         
42781         selectedClass: 'active',
42782         
42783         invalidClass : "has-warning",
42784         
42785         validClass: 'has-success',
42786         
42787         allowed: '0123456789',
42788         
42789         max_length: 15,
42790         
42791         /**
42792          * @cfg {String} defaultDialCode The default dial code when initializing the input
42793          */
42794         defaultDialCode: '+852',
42795         
42796         /**
42797          * @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
42798          */
42799         preferedCountries: false,
42800         
42801         getAutoCreate : function()
42802         {
42803             var data = Roo.bootstrap.PhoneInputData();
42804             var align = this.labelAlign || this.parentLabelAlign();
42805             var id = Roo.id();
42806             
42807             this.allCountries = [];
42808             this.dialCodeMapping = [];
42809             
42810             for (var i = 0; i < data.length; i++) {
42811               var c = data[i];
42812               this.allCountries[i] = {
42813                 name: c[0],
42814                 iso2: c[1],
42815                 dialCode: c[2],
42816                 priority: c[3] || 0,
42817                 areaCodes: c[4] || null
42818               };
42819               this.dialCodeMapping[c[2]] = {
42820                   name: c[0],
42821                   iso2: c[1],
42822                   priority: c[3] || 0,
42823                   areaCodes: c[4] || null
42824               };
42825             }
42826             
42827             var cfg = {
42828                 cls: 'form-group',
42829                 cn: []
42830             };
42831             
42832             var input =  {
42833                 tag: 'input',
42834                 id : id,
42835                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42836                 maxlength: this.max_length,
42837                 cls : 'form-control tel-input',
42838                 autocomplete: 'new-password'
42839             };
42840             
42841             var hiddenInput = {
42842                 tag: 'input',
42843                 type: 'hidden',
42844                 cls: 'hidden-tel-input'
42845             };
42846             
42847             if (this.name) {
42848                 hiddenInput.name = this.name;
42849             }
42850             
42851             if (this.disabled) {
42852                 input.disabled = true;
42853             }
42854             
42855             var flag_container = {
42856                 tag: 'div',
42857                 cls: 'flag-box',
42858                 cn: [
42859                     {
42860                         tag: 'div',
42861                         cls: 'flag'
42862                     },
42863                     {
42864                         tag: 'div',
42865                         cls: 'caret'
42866                     }
42867                 ]
42868             };
42869             
42870             var box = {
42871                 tag: 'div',
42872                 cls: this.hasFeedback ? 'has-feedback' : '',
42873                 cn: [
42874                     hiddenInput,
42875                     input,
42876                     {
42877                         tag: 'input',
42878                         cls: 'dial-code-holder',
42879                         disabled: true
42880                     }
42881                 ]
42882             };
42883             
42884             var container = {
42885                 cls: 'roo-select2-container input-group',
42886                 cn: [
42887                     flag_container,
42888                     box
42889                 ]
42890             };
42891             
42892             if (this.fieldLabel.length) {
42893                 var indicator = {
42894                     tag: 'i',
42895                     tooltip: 'This field is required'
42896                 };
42897                 
42898                 var label = {
42899                     tag: 'label',
42900                     'for':  id,
42901                     cls: 'control-label',
42902                     cn: []
42903                 };
42904                 
42905                 var label_text = {
42906                     tag: 'span',
42907                     html: this.fieldLabel
42908                 };
42909                 
42910                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42911                 label.cn = [
42912                     indicator,
42913                     label_text
42914                 ];
42915                 
42916                 if(this.indicatorpos == 'right') {
42917                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42918                     label.cn = [
42919                         label_text,
42920                         indicator
42921                     ];
42922                 }
42923                 
42924                 if(align == 'left') {
42925                     container = {
42926                         tag: 'div',
42927                         cn: [
42928                             container
42929                         ]
42930                     };
42931                     
42932                     if(this.labelWidth > 12){
42933                         label.style = "width: " + this.labelWidth + 'px';
42934                     }
42935                     if(this.labelWidth < 13 && this.labelmd == 0){
42936                         this.labelmd = this.labelWidth;
42937                     }
42938                     if(this.labellg > 0){
42939                         label.cls += ' col-lg-' + this.labellg;
42940                         input.cls += ' col-lg-' + (12 - this.labellg);
42941                     }
42942                     if(this.labelmd > 0){
42943                         label.cls += ' col-md-' + this.labelmd;
42944                         container.cls += ' col-md-' + (12 - this.labelmd);
42945                     }
42946                     if(this.labelsm > 0){
42947                         label.cls += ' col-sm-' + this.labelsm;
42948                         container.cls += ' col-sm-' + (12 - this.labelsm);
42949                     }
42950                     if(this.labelxs > 0){
42951                         label.cls += ' col-xs-' + this.labelxs;
42952                         container.cls += ' col-xs-' + (12 - this.labelxs);
42953                     }
42954                 }
42955             }
42956             
42957             cfg.cn = [
42958                 label,
42959                 container
42960             ];
42961             
42962             var settings = this;
42963             
42964             ['xs','sm','md','lg'].map(function(size){
42965                 if (settings[size]) {
42966                     cfg.cls += ' col-' + size + '-' + settings[size];
42967                 }
42968             });
42969             
42970             this.store = new Roo.data.Store({
42971                 proxy : new Roo.data.MemoryProxy({}),
42972                 reader : new Roo.data.JsonReader({
42973                     fields : [
42974                         {
42975                             'name' : 'name',
42976                             'type' : 'string'
42977                         },
42978                         {
42979                             'name' : 'iso2',
42980                             'type' : 'string'
42981                         },
42982                         {
42983                             'name' : 'dialCode',
42984                             'type' : 'string'
42985                         },
42986                         {
42987                             'name' : 'priority',
42988                             'type' : 'string'
42989                         },
42990                         {
42991                             'name' : 'areaCodes',
42992                             'type' : 'string'
42993                         }
42994                     ]
42995                 })
42996             });
42997             
42998             if(!this.preferedCountries) {
42999                 this.preferedCountries = [
43000                     'hk',
43001                     'gb',
43002                     'us'
43003                 ];
43004             }
43005             
43006             var p = this.preferedCountries.reverse();
43007             
43008             if(p) {
43009                 for (var i = 0; i < p.length; i++) {
43010                     for (var j = 0; j < this.allCountries.length; j++) {
43011                         if(this.allCountries[j].iso2 == p[i]) {
43012                             var t = this.allCountries[j];
43013                             this.allCountries.splice(j,1);
43014                             this.allCountries.unshift(t);
43015                         }
43016                     } 
43017                 }
43018             }
43019             
43020             this.store.proxy.data = {
43021                 success: true,
43022                 data: this.allCountries
43023             };
43024             
43025             return cfg;
43026         },
43027         
43028         initEvents : function()
43029         {
43030             this.createList();
43031             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43032             
43033             this.indicator = this.indicatorEl();
43034             this.flag = this.flagEl();
43035             this.dialCodeHolder = this.dialCodeHolderEl();
43036             
43037             this.trigger = this.el.select('div.flag-box',true).first();
43038             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43039             
43040             var _this = this;
43041             
43042             (function(){
43043                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43044                 _this.list.setWidth(lw);
43045             }).defer(100);
43046             
43047             this.list.on('mouseover', this.onViewOver, this);
43048             this.list.on('mousemove', this.onViewMove, this);
43049             this.inputEl().on("keyup", this.onKeyUp, this);
43050             this.inputEl().on("keypress", this.onKeyPress, this);
43051             
43052             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43053
43054             this.view = new Roo.View(this.list, this.tpl, {
43055                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43056             });
43057             
43058             this.view.on('click', this.onViewClick, this);
43059             this.setValue(this.defaultDialCode);
43060         },
43061         
43062         onTriggerClick : function(e)
43063         {
43064             Roo.log('trigger click');
43065             if(this.disabled){
43066                 return;
43067             }
43068             
43069             if(this.isExpanded()){
43070                 this.collapse();
43071                 this.hasFocus = false;
43072             }else {
43073                 this.store.load({});
43074                 this.hasFocus = true;
43075                 this.expand();
43076             }
43077         },
43078         
43079         isExpanded : function()
43080         {
43081             return this.list.isVisible();
43082         },
43083         
43084         collapse : function()
43085         {
43086             if(!this.isExpanded()){
43087                 return;
43088             }
43089             this.list.hide();
43090             Roo.get(document).un('mousedown', this.collapseIf, this);
43091             Roo.get(document).un('mousewheel', this.collapseIf, this);
43092             this.fireEvent('collapse', this);
43093             this.validate();
43094         },
43095         
43096         expand : function()
43097         {
43098             Roo.log('expand');
43099
43100             if(this.isExpanded() || !this.hasFocus){
43101                 return;
43102             }
43103             
43104             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43105             this.list.setWidth(lw);
43106             
43107             this.list.show();
43108             this.restrictHeight();
43109             
43110             Roo.get(document).on('mousedown', this.collapseIf, this);
43111             Roo.get(document).on('mousewheel', this.collapseIf, this);
43112             
43113             this.fireEvent('expand', this);
43114         },
43115         
43116         restrictHeight : function()
43117         {
43118             this.list.alignTo(this.inputEl(), this.listAlign);
43119             this.list.alignTo(this.inputEl(), this.listAlign);
43120         },
43121         
43122         onViewOver : function(e, t)
43123         {
43124             if(this.inKeyMode){
43125                 return;
43126             }
43127             var item = this.view.findItemFromChild(t);
43128             
43129             if(item){
43130                 var index = this.view.indexOf(item);
43131                 this.select(index, false);
43132             }
43133         },
43134
43135         // private
43136         onViewClick : function(view, doFocus, el, e)
43137         {
43138             var index = this.view.getSelectedIndexes()[0];
43139             
43140             var r = this.store.getAt(index);
43141             
43142             if(r){
43143                 this.onSelect(r, index);
43144             }
43145             if(doFocus !== false && !this.blockFocus){
43146                 this.inputEl().focus();
43147             }
43148         },
43149         
43150         onViewMove : function(e, t)
43151         {
43152             this.inKeyMode = false;
43153         },
43154         
43155         select : function(index, scrollIntoView)
43156         {
43157             this.selectedIndex = index;
43158             this.view.select(index);
43159             if(scrollIntoView !== false){
43160                 var el = this.view.getNode(index);
43161                 if(el){
43162                     this.list.scrollChildIntoView(el, false);
43163                 }
43164             }
43165         },
43166         
43167         createList : function()
43168         {
43169             this.list = Roo.get(document.body).createChild({
43170                 tag: 'ul',
43171                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43172                 style: 'display:none'
43173             });
43174             
43175             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43176         },
43177         
43178         collapseIf : function(e)
43179         {
43180             var in_combo  = e.within(this.el);
43181             var in_list =  e.within(this.list);
43182             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43183             
43184             if (in_combo || in_list || is_list) {
43185                 return;
43186             }
43187             this.collapse();
43188         },
43189         
43190         onSelect : function(record, index)
43191         {
43192             if(this.fireEvent('beforeselect', this, record, index) !== false){
43193                 
43194                 this.setFlagClass(record.data.iso2);
43195                 this.setDialCode(record.data.dialCode);
43196                 this.hasFocus = false;
43197                 this.collapse();
43198                 this.fireEvent('select', this, record, index);
43199             }
43200         },
43201         
43202         flagEl : function()
43203         {
43204             var flag = this.el.select('div.flag',true).first();
43205             if(!flag){
43206                 return false;
43207             }
43208             return flag;
43209         },
43210         
43211         dialCodeHolderEl : function()
43212         {
43213             var d = this.el.select('input.dial-code-holder',true).first();
43214             if(!d){
43215                 return false;
43216             }
43217             return d;
43218         },
43219         
43220         setDialCode : function(v)
43221         {
43222             this.dialCodeHolder.dom.value = '+'+v;
43223         },
43224         
43225         setFlagClass : function(n)
43226         {
43227             this.flag.dom.className = 'flag '+n;
43228         },
43229         
43230         getValue : function()
43231         {
43232             var v = this.inputEl().getValue();
43233             if(this.dialCodeHolder) {
43234                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43235             }
43236             return v;
43237         },
43238         
43239         setValue : function(v)
43240         {
43241             var d = this.getDialCode(v);
43242             
43243             //invalid dial code
43244             if(v.length == 0 || !d || d.length == 0) {
43245                 if(this.rendered){
43246                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43247                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43248                 }
43249                 return;
43250             }
43251             
43252             //valid dial code
43253             this.setFlagClass(this.dialCodeMapping[d].iso2);
43254             this.setDialCode(d);
43255             this.inputEl().dom.value = v.replace('+'+d,'');
43256             this.hiddenEl().dom.value = this.getValue();
43257             
43258             this.validate();
43259         },
43260         
43261         getDialCode : function(v)
43262         {
43263             v = v ||  '';
43264             
43265             if (v.length == 0) {
43266                 return this.dialCodeHolder.dom.value;
43267             }
43268             
43269             var dialCode = "";
43270             if (v.charAt(0) != "+") {
43271                 return false;
43272             }
43273             var numericChars = "";
43274             for (var i = 1; i < v.length; i++) {
43275               var c = v.charAt(i);
43276               if (!isNaN(c)) {
43277                 numericChars += c;
43278                 if (this.dialCodeMapping[numericChars]) {
43279                   dialCode = v.substr(1, i);
43280                 }
43281                 if (numericChars.length == 4) {
43282                   break;
43283                 }
43284               }
43285             }
43286             return dialCode;
43287         },
43288         
43289         reset : function()
43290         {
43291             this.setValue(this.defaultDialCode);
43292             this.validate();
43293         },
43294         
43295         hiddenEl : function()
43296         {
43297             return this.el.select('input.hidden-tel-input',true).first();
43298         },
43299         
43300         // after setting val
43301         onKeyUp : function(e){
43302             this.setValue(this.getValue());
43303         },
43304         
43305         onKeyPress : function(e){
43306             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43307                 e.stopEvent();
43308             }
43309         }
43310         
43311 });
43312 /**
43313  * @class Roo.bootstrap.MoneyField
43314  * @extends Roo.bootstrap.ComboBox
43315  * Bootstrap MoneyField class
43316  * 
43317  * @constructor
43318  * Create a new MoneyField.
43319  * @param {Object} config Configuration options
43320  */
43321
43322 Roo.bootstrap.MoneyField = function(config) {
43323     
43324     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43325     
43326 };
43327
43328 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43329     
43330     /**
43331      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43332      */
43333     allowDecimals : true,
43334     /**
43335      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43336      */
43337     decimalSeparator : ".",
43338     /**
43339      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43340      */
43341     decimalPrecision : 0,
43342     /**
43343      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43344      */
43345     allowNegative : true,
43346     /**
43347      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43348      */
43349     allowZero: true,
43350     /**
43351      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43352      */
43353     minValue : Number.NEGATIVE_INFINITY,
43354     /**
43355      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43356      */
43357     maxValue : Number.MAX_VALUE,
43358     /**
43359      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43360      */
43361     minText : "The minimum value for this field is {0}",
43362     /**
43363      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43364      */
43365     maxText : "The maximum value for this field is {0}",
43366     /**
43367      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43368      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43369      */
43370     nanText : "{0} is not a valid number",
43371     /**
43372      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43373      */
43374     castInt : true,
43375     /**
43376      * @cfg {String} defaults currency of the MoneyField
43377      * value should be in lkey
43378      */
43379     defaultCurrency : false,
43380     /**
43381      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43382      */
43383     thousandsDelimiter : false,
43384     /**
43385      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43386      */
43387     max_length: false,
43388     
43389     inputlg : 9,
43390     inputmd : 9,
43391     inputsm : 9,
43392     inputxs : 6,
43393     
43394     store : false,
43395     
43396     getAutoCreate : function()
43397     {
43398         var align = this.labelAlign || this.parentLabelAlign();
43399         
43400         var id = Roo.id();
43401
43402         var cfg = {
43403             cls: 'form-group',
43404             cn: []
43405         };
43406
43407         var input =  {
43408             tag: 'input',
43409             id : id,
43410             cls : 'form-control roo-money-amount-input',
43411             autocomplete: 'new-password'
43412         };
43413         
43414         var hiddenInput = {
43415             tag: 'input',
43416             type: 'hidden',
43417             id: Roo.id(),
43418             cls: 'hidden-number-input'
43419         };
43420         
43421         if(this.max_length) {
43422             input.maxlength = this.max_length; 
43423         }
43424         
43425         if (this.name) {
43426             hiddenInput.name = this.name;
43427         }
43428
43429         if (this.disabled) {
43430             input.disabled = true;
43431         }
43432
43433         var clg = 12 - this.inputlg;
43434         var cmd = 12 - this.inputmd;
43435         var csm = 12 - this.inputsm;
43436         var cxs = 12 - this.inputxs;
43437         
43438         var container = {
43439             tag : 'div',
43440             cls : 'row roo-money-field',
43441             cn : [
43442                 {
43443                     tag : 'div',
43444                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43445                     cn : [
43446                         {
43447                             tag : 'div',
43448                             cls: 'roo-select2-container input-group',
43449                             cn: [
43450                                 {
43451                                     tag : 'input',
43452                                     cls : 'form-control roo-money-currency-input',
43453                                     autocomplete: 'new-password',
43454                                     readOnly : 1,
43455                                     name : this.currencyName
43456                                 },
43457                                 {
43458                                     tag :'span',
43459                                     cls : 'input-group-addon',
43460                                     cn : [
43461                                         {
43462                                             tag: 'span',
43463                                             cls: 'caret'
43464                                         }
43465                                     ]
43466                                 }
43467                             ]
43468                         }
43469                     ]
43470                 },
43471                 {
43472                     tag : 'div',
43473                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43474                     cn : [
43475                         {
43476                             tag: 'div',
43477                             cls: this.hasFeedback ? 'has-feedback' : '',
43478                             cn: [
43479                                 input
43480                             ]
43481                         }
43482                     ]
43483                 }
43484             ]
43485             
43486         };
43487         
43488         if (this.fieldLabel.length) {
43489             var indicator = {
43490                 tag: 'i',
43491                 tooltip: 'This field is required'
43492             };
43493
43494             var label = {
43495                 tag: 'label',
43496                 'for':  id,
43497                 cls: 'control-label',
43498                 cn: []
43499             };
43500
43501             var label_text = {
43502                 tag: 'span',
43503                 html: this.fieldLabel
43504             };
43505
43506             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43507             label.cn = [
43508                 indicator,
43509                 label_text
43510             ];
43511
43512             if(this.indicatorpos == 'right') {
43513                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43514                 label.cn = [
43515                     label_text,
43516                     indicator
43517                 ];
43518             }
43519
43520             if(align == 'left') {
43521                 container = {
43522                     tag: 'div',
43523                     cn: [
43524                         container
43525                     ]
43526                 };
43527
43528                 if(this.labelWidth > 12){
43529                     label.style = "width: " + this.labelWidth + 'px';
43530                 }
43531                 if(this.labelWidth < 13 && this.labelmd == 0){
43532                     this.labelmd = this.labelWidth;
43533                 }
43534                 if(this.labellg > 0){
43535                     label.cls += ' col-lg-' + this.labellg;
43536                     input.cls += ' col-lg-' + (12 - this.labellg);
43537                 }
43538                 if(this.labelmd > 0){
43539                     label.cls += ' col-md-' + this.labelmd;
43540                     container.cls += ' col-md-' + (12 - this.labelmd);
43541                 }
43542                 if(this.labelsm > 0){
43543                     label.cls += ' col-sm-' + this.labelsm;
43544                     container.cls += ' col-sm-' + (12 - this.labelsm);
43545                 }
43546                 if(this.labelxs > 0){
43547                     label.cls += ' col-xs-' + this.labelxs;
43548                     container.cls += ' col-xs-' + (12 - this.labelxs);
43549                 }
43550             }
43551         }
43552
43553         cfg.cn = [
43554             label,
43555             container,
43556             hiddenInput
43557         ];
43558         
43559         var settings = this;
43560
43561         ['xs','sm','md','lg'].map(function(size){
43562             if (settings[size]) {
43563                 cfg.cls += ' col-' + size + '-' + settings[size];
43564             }
43565         });
43566         
43567         return cfg;
43568     },
43569     
43570     initEvents : function()
43571     {
43572         this.indicator = this.indicatorEl();
43573         
43574         this.initCurrencyEvent();
43575         
43576         this.initNumberEvent();
43577     },
43578     
43579     initCurrencyEvent : function()
43580     {
43581         if (!this.store) {
43582             throw "can not find store for combo";
43583         }
43584         
43585         this.store = Roo.factory(this.store, Roo.data);
43586         this.store.parent = this;
43587         
43588         this.createList();
43589         
43590         this.triggerEl = this.el.select('.input-group-addon', true).first();
43591         
43592         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43593         
43594         var _this = this;
43595         
43596         (function(){
43597             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43598             _this.list.setWidth(lw);
43599         }).defer(100);
43600         
43601         this.list.on('mouseover', this.onViewOver, this);
43602         this.list.on('mousemove', this.onViewMove, this);
43603         this.list.on('scroll', this.onViewScroll, this);
43604         
43605         if(!this.tpl){
43606             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43607         }
43608         
43609         this.view = new Roo.View(this.list, this.tpl, {
43610             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43611         });
43612         
43613         this.view.on('click', this.onViewClick, this);
43614         
43615         this.store.on('beforeload', this.onBeforeLoad, this);
43616         this.store.on('load', this.onLoad, this);
43617         this.store.on('loadexception', this.onLoadException, this);
43618         
43619         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43620             "up" : function(e){
43621                 this.inKeyMode = true;
43622                 this.selectPrev();
43623             },
43624
43625             "down" : function(e){
43626                 if(!this.isExpanded()){
43627                     this.onTriggerClick();
43628                 }else{
43629                     this.inKeyMode = true;
43630                     this.selectNext();
43631                 }
43632             },
43633
43634             "enter" : function(e){
43635                 this.collapse();
43636                 
43637                 if(this.fireEvent("specialkey", this, e)){
43638                     this.onViewClick(false);
43639                 }
43640                 
43641                 return true;
43642             },
43643
43644             "esc" : function(e){
43645                 this.collapse();
43646             },
43647
43648             "tab" : function(e){
43649                 this.collapse();
43650                 
43651                 if(this.fireEvent("specialkey", this, e)){
43652                     this.onViewClick(false);
43653                 }
43654                 
43655                 return true;
43656             },
43657
43658             scope : this,
43659
43660             doRelay : function(foo, bar, hname){
43661                 if(hname == 'down' || this.scope.isExpanded()){
43662                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43663                 }
43664                 return true;
43665             },
43666
43667             forceKeyDown: true
43668         });
43669         
43670         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43671         
43672     },
43673     
43674     initNumberEvent : function(e)
43675     {
43676         this.inputEl().on("keydown" , this.fireKey,  this);
43677         this.inputEl().on("focus", this.onFocus,  this);
43678         this.inputEl().on("blur", this.onBlur,  this);
43679         
43680         this.inputEl().relayEvent('keyup', this);
43681         
43682         if(this.indicator){
43683             this.indicator.addClass('invisible');
43684         }
43685  
43686         this.originalValue = this.getValue();
43687         
43688         if(this.validationEvent == 'keyup'){
43689             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43690             this.inputEl().on('keyup', this.filterValidation, this);
43691         }
43692         else if(this.validationEvent !== false){
43693             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43694         }
43695         
43696         if(this.selectOnFocus){
43697             this.on("focus", this.preFocus, this);
43698             
43699         }
43700         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43701             this.inputEl().on("keypress", this.filterKeys, this);
43702         } else {
43703             this.inputEl().relayEvent('keypress', this);
43704         }
43705         
43706         var allowed = "0123456789";
43707         
43708         if(this.allowDecimals){
43709             allowed += this.decimalSeparator;
43710         }
43711         
43712         if(this.allowNegative){
43713             allowed += "-";
43714         }
43715         
43716         if(this.thousandsDelimiter) {
43717             allowed += ",";
43718         }
43719         
43720         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43721         
43722         var keyPress = function(e){
43723             
43724             var k = e.getKey();
43725             
43726             var c = e.getCharCode();
43727             
43728             if(
43729                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43730                     allowed.indexOf(String.fromCharCode(c)) === -1
43731             ){
43732                 e.stopEvent();
43733                 return;
43734             }
43735             
43736             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43737                 return;
43738             }
43739             
43740             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43741                 e.stopEvent();
43742             }
43743         };
43744         
43745         this.inputEl().on("keypress", keyPress, this);
43746         
43747     },
43748     
43749     onTriggerClick : function(e)
43750     {   
43751         if(this.disabled){
43752             return;
43753         }
43754         
43755         this.page = 0;
43756         this.loadNext = false;
43757         
43758         if(this.isExpanded()){
43759             this.collapse();
43760             return;
43761         }
43762         
43763         this.hasFocus = true;
43764         
43765         if(this.triggerAction == 'all') {
43766             this.doQuery(this.allQuery, true);
43767             return;
43768         }
43769         
43770         this.doQuery(this.getRawValue());
43771     },
43772     
43773     getCurrency : function()
43774     {   
43775         var v = this.currencyEl().getValue();
43776         
43777         return v;
43778     },
43779     
43780     restrictHeight : function()
43781     {
43782         this.list.alignTo(this.currencyEl(), this.listAlign);
43783         this.list.alignTo(this.currencyEl(), this.listAlign);
43784     },
43785     
43786     onViewClick : function(view, doFocus, el, e)
43787     {
43788         var index = this.view.getSelectedIndexes()[0];
43789         
43790         var r = this.store.getAt(index);
43791         
43792         if(r){
43793             this.onSelect(r, index);
43794         }
43795     },
43796     
43797     onSelect : function(record, index){
43798         
43799         if(this.fireEvent('beforeselect', this, record, index) !== false){
43800         
43801             this.setFromCurrencyData(index > -1 ? record.data : false);
43802             
43803             this.collapse();
43804             
43805             this.fireEvent('select', this, record, index);
43806         }
43807     },
43808     
43809     setFromCurrencyData : function(o)
43810     {
43811         var currency = '';
43812         
43813         this.lastCurrency = o;
43814         
43815         if (this.currencyField) {
43816             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43817         } else {
43818             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43819         }
43820         
43821         this.lastSelectionText = currency;
43822         
43823         //setting default currency
43824         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43825             this.setCurrency(this.defaultCurrency);
43826             return;
43827         }
43828         
43829         this.setCurrency(currency);
43830     },
43831     
43832     setFromData : function(o)
43833     {
43834         var c = {};
43835         
43836         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43837         
43838         this.setFromCurrencyData(c);
43839         
43840         var value = '';
43841         
43842         if (this.name) {
43843             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43844         } else {
43845             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43846         }
43847         
43848         this.setValue(value);
43849         
43850     },
43851     
43852     setCurrency : function(v)
43853     {   
43854         this.currencyValue = v;
43855         
43856         if(this.rendered){
43857             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43858             this.validate();
43859         }
43860     },
43861     
43862     setValue : function(v)
43863     {
43864         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43865         
43866         this.value = v;
43867         
43868         if(this.rendered){
43869             
43870             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43871             
43872             this.inputEl().dom.value = (v == '') ? '' :
43873                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43874             
43875             if(!this.allowZero && v === '0') {
43876                 this.hiddenEl().dom.value = '';
43877                 this.inputEl().dom.value = '';
43878             }
43879             
43880             this.validate();
43881         }
43882     },
43883     
43884     getRawValue : function()
43885     {
43886         var v = this.inputEl().getValue();
43887         
43888         return v;
43889     },
43890     
43891     getValue : function()
43892     {
43893         return this.fixPrecision(this.parseValue(this.getRawValue()));
43894     },
43895     
43896     parseValue : function(value)
43897     {
43898         if(this.thousandsDelimiter) {
43899             value += "";
43900             r = new RegExp(",", "g");
43901             value = value.replace(r, "");
43902         }
43903         
43904         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43905         return isNaN(value) ? '' : value;
43906         
43907     },
43908     
43909     fixPrecision : function(value)
43910     {
43911         if(this.thousandsDelimiter) {
43912             value += "";
43913             r = new RegExp(",", "g");
43914             value = value.replace(r, "");
43915         }
43916         
43917         var nan = isNaN(value);
43918         
43919         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43920             return nan ? '' : value;
43921         }
43922         return parseFloat(value).toFixed(this.decimalPrecision);
43923     },
43924     
43925     decimalPrecisionFcn : function(v)
43926     {
43927         return Math.floor(v);
43928     },
43929     
43930     validateValue : function(value)
43931     {
43932         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43933             return false;
43934         }
43935         
43936         var num = this.parseValue(value);
43937         
43938         if(isNaN(num)){
43939             this.markInvalid(String.format(this.nanText, value));
43940             return false;
43941         }
43942         
43943         if(num < this.minValue){
43944             this.markInvalid(String.format(this.minText, this.minValue));
43945             return false;
43946         }
43947         
43948         if(num > this.maxValue){
43949             this.markInvalid(String.format(this.maxText, this.maxValue));
43950             return false;
43951         }
43952         
43953         return true;
43954     },
43955     
43956     validate : function()
43957     {
43958         if(this.disabled || this.allowBlank){
43959             this.markValid();
43960             return true;
43961         }
43962         
43963         var currency = this.getCurrency();
43964         
43965         if(this.validateValue(this.getRawValue()) && currency.length){
43966             this.markValid();
43967             return true;
43968         }
43969         
43970         this.markInvalid();
43971         return false;
43972     },
43973     
43974     getName: function()
43975     {
43976         return this.name;
43977     },
43978     
43979     beforeBlur : function()
43980     {
43981         if(!this.castInt){
43982             return;
43983         }
43984         
43985         var v = this.parseValue(this.getRawValue());
43986         
43987         if(v || v == 0){
43988             this.setValue(v);
43989         }
43990     },
43991     
43992     onBlur : function()
43993     {
43994         this.beforeBlur();
43995         
43996         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43997             //this.el.removeClass(this.focusClass);
43998         }
43999         
44000         this.hasFocus = false;
44001         
44002         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44003             this.validate();
44004         }
44005         
44006         var v = this.getValue();
44007         
44008         if(String(v) !== String(this.startValue)){
44009             this.fireEvent('change', this, v, this.startValue);
44010         }
44011         
44012         this.fireEvent("blur", this);
44013     },
44014     
44015     inputEl : function()
44016     {
44017         return this.el.select('.roo-money-amount-input', true).first();
44018     },
44019     
44020     currencyEl : function()
44021     {
44022         return this.el.select('.roo-money-currency-input', true).first();
44023     },
44024     
44025     hiddenEl : function()
44026     {
44027         return this.el.select('input.hidden-number-input',true).first();
44028     }
44029     
44030 });/**
44031  * @class Roo.bootstrap.BezierSignature
44032  * @extends Roo.bootstrap.Component
44033  * Bootstrap BezierSignature class
44034  * This script refer to:
44035  *    Title: Signature Pad
44036  *    Author: szimek
44037  *    Availability: https://github.com/szimek/signature_pad
44038  *
44039  * @constructor
44040  * Create a new BezierSignature
44041  * @param {Object} config The config object
44042  */
44043
44044 Roo.bootstrap.BezierSignature = function(config){
44045     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44046     this.addEvents({
44047         "resize" : true
44048     });
44049 };
44050
44051 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44052 {
44053      
44054     curve_data: [],
44055     
44056     is_empty: true,
44057     
44058     mouse_btn_down: true,
44059     
44060     /**
44061      * @cfg {int} canvas height
44062      */
44063     canvas_height: '200px',
44064     
44065     /**
44066      * @cfg {float|function} Radius of a single dot.
44067      */ 
44068     dot_size: false,
44069     
44070     /**
44071      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44072      */
44073     min_width: 0.5,
44074     
44075     /**
44076      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44077      */
44078     max_width: 2.5,
44079     
44080     /**
44081      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44082      */
44083     throttle: 16,
44084     
44085     /**
44086      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44087      */
44088     min_distance: 5,
44089     
44090     /**
44091      * @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.
44092      */
44093     bg_color: 'rgba(0, 0, 0, 0)',
44094     
44095     /**
44096      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44097      */
44098     dot_color: 'black',
44099     
44100     /**
44101      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44102      */ 
44103     velocity_filter_weight: 0.7,
44104     
44105     /**
44106      * @cfg {function} Callback when stroke begin. 
44107      */
44108     onBegin: false,
44109     
44110     /**
44111      * @cfg {function} Callback when stroke end.
44112      */
44113     onEnd: false,
44114     
44115     getAutoCreate : function()
44116     {
44117         var cls = 'roo-signature column';
44118         
44119         if(this.cls){
44120             cls += ' ' + this.cls;
44121         }
44122         
44123         var col_sizes = [
44124             'lg',
44125             'md',
44126             'sm',
44127             'xs'
44128         ];
44129         
44130         for(var i = 0; i < col_sizes.length; i++) {
44131             if(this[col_sizes[i]]) {
44132                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44133             }
44134         }
44135         
44136         var cfg = {
44137             tag: 'div',
44138             cls: cls,
44139             cn: [
44140                 {
44141                     tag: 'div',
44142                     cls: 'roo-signature-body',
44143                     cn: [
44144                         {
44145                             tag: 'canvas',
44146                             cls: 'roo-signature-body-canvas',
44147                             height: this.canvas_height,
44148                             width: this.canvas_width
44149                         }
44150                     ]
44151                 },
44152                 {
44153                     tag: 'input',
44154                     type: 'file',
44155                     style: 'display: none'
44156                 }
44157             ]
44158         };
44159         
44160         return cfg;
44161     },
44162     
44163     initEvents: function() 
44164     {
44165         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44166         
44167         var canvas = this.canvasEl();
44168         
44169         // mouse && touch event swapping...
44170         canvas.dom.style.touchAction = 'none';
44171         canvas.dom.style.msTouchAction = 'none';
44172         
44173         this.mouse_btn_down = false;
44174         canvas.on('mousedown', this._handleMouseDown, this);
44175         canvas.on('mousemove', this._handleMouseMove, this);
44176         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44177         
44178         if (window.PointerEvent) {
44179             canvas.on('pointerdown', this._handleMouseDown, this);
44180             canvas.on('pointermove', this._handleMouseMove, this);
44181             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44182         }
44183         
44184         if ('ontouchstart' in window) {
44185             canvas.on('touchstart', this._handleTouchStart, this);
44186             canvas.on('touchmove', this._handleTouchMove, this);
44187             canvas.on('touchend', this._handleTouchEnd, this);
44188         }
44189         
44190         Roo.EventManager.onWindowResize(this.resize, this, true);
44191         
44192         // file input event
44193         this.fileEl().on('change', this.uploadImage, this);
44194         
44195         this.clear();
44196         
44197         this.resize();
44198     },
44199     
44200     resize: function(){
44201         
44202         var canvas = this.canvasEl().dom;
44203         var ctx = this.canvasElCtx();
44204         var img_data = false;
44205         
44206         if(canvas.width > 0) {
44207             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44208         }
44209         // setting canvas width will clean img data
44210         canvas.width = 0;
44211         
44212         var style = window.getComputedStyle ? 
44213             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44214             
44215         var padding_left = parseInt(style.paddingLeft) || 0;
44216         var padding_right = parseInt(style.paddingRight) || 0;
44217         
44218         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44219         
44220         if(img_data) {
44221             ctx.putImageData(img_data, 0, 0);
44222         }
44223     },
44224     
44225     _handleMouseDown: function(e)
44226     {
44227         if (e.browserEvent.which === 1) {
44228             this.mouse_btn_down = true;
44229             this.strokeBegin(e);
44230         }
44231     },
44232     
44233     _handleMouseMove: function (e)
44234     {
44235         if (this.mouse_btn_down) {
44236             this.strokeMoveUpdate(e);
44237         }
44238     },
44239     
44240     _handleMouseUp: function (e)
44241     {
44242         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44243             this.mouse_btn_down = false;
44244             this.strokeEnd(e);
44245         }
44246     },
44247     
44248     _handleTouchStart: function (e) {
44249         
44250         e.preventDefault();
44251         if (e.browserEvent.targetTouches.length === 1) {
44252             // var touch = e.browserEvent.changedTouches[0];
44253             // this.strokeBegin(touch);
44254             
44255              this.strokeBegin(e); // assume e catching the correct xy...
44256         }
44257     },
44258     
44259     _handleTouchMove: function (e) {
44260         e.preventDefault();
44261         // var touch = event.targetTouches[0];
44262         // _this._strokeMoveUpdate(touch);
44263         this.strokeMoveUpdate(e);
44264     },
44265     
44266     _handleTouchEnd: function (e) {
44267         var wasCanvasTouched = e.target === this.canvasEl().dom;
44268         if (wasCanvasTouched) {
44269             e.preventDefault();
44270             // var touch = event.changedTouches[0];
44271             // _this._strokeEnd(touch);
44272             this.strokeEnd(e);
44273         }
44274     },
44275     
44276     reset: function () {
44277         this._lastPoints = [];
44278         this._lastVelocity = 0;
44279         this._lastWidth = (this.min_width + this.max_width) / 2;
44280         this.canvasElCtx().fillStyle = this.dot_color;
44281     },
44282     
44283     strokeMoveUpdate: function(e)
44284     {
44285         this.strokeUpdate(e);
44286         
44287         if (this.throttle) {
44288             this.throttleStroke(this.strokeUpdate, this.throttle);
44289         }
44290         else {
44291             this.strokeUpdate(e);
44292         }
44293     },
44294     
44295     strokeBegin: function(e)
44296     {
44297         var newPointGroup = {
44298             color: this.dot_color,
44299             points: []
44300         };
44301         
44302         if (typeof this.onBegin === 'function') {
44303             this.onBegin(e);
44304         }
44305         
44306         this.curve_data.push(newPointGroup);
44307         this.reset();
44308         this.strokeUpdate(e);
44309     },
44310     
44311     strokeUpdate: function(e)
44312     {
44313         var rect = this.canvasEl().dom.getBoundingClientRect();
44314         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44315         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44316         var lastPoints = lastPointGroup.points;
44317         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44318         var isLastPointTooClose = lastPoint
44319             ? point.distanceTo(lastPoint) <= this.min_distance
44320             : false;
44321         var color = lastPointGroup.color;
44322         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44323             var curve = this.addPoint(point);
44324             if (!lastPoint) {
44325                 this.drawDot({color: color, point: point});
44326             }
44327             else if (curve) {
44328                 this.drawCurve({color: color, curve: curve});
44329             }
44330             lastPoints.push({
44331                 time: point.time,
44332                 x: point.x,
44333                 y: point.y
44334             });
44335         }
44336     },
44337     
44338     strokeEnd: function(e)
44339     {
44340         this.strokeUpdate(e);
44341         if (typeof this.onEnd === 'function') {
44342             this.onEnd(e);
44343         }
44344     },
44345     
44346     addPoint:  function (point) {
44347         var _lastPoints = this._lastPoints;
44348         _lastPoints.push(point);
44349         if (_lastPoints.length > 2) {
44350             if (_lastPoints.length === 3) {
44351                 _lastPoints.unshift(_lastPoints[0]);
44352             }
44353             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44354             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44355             _lastPoints.shift();
44356             return curve;
44357         }
44358         return null;
44359     },
44360     
44361     calculateCurveWidths: function (startPoint, endPoint) {
44362         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44363             (1 - this.velocity_filter_weight) * this._lastVelocity;
44364
44365         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44366         var widths = {
44367             end: newWidth,
44368             start: this._lastWidth
44369         };
44370         
44371         this._lastVelocity = velocity;
44372         this._lastWidth = newWidth;
44373         return widths;
44374     },
44375     
44376     drawDot: function (_a) {
44377         var color = _a.color, point = _a.point;
44378         var ctx = this.canvasElCtx();
44379         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44380         ctx.beginPath();
44381         this.drawCurveSegment(point.x, point.y, width);
44382         ctx.closePath();
44383         ctx.fillStyle = color;
44384         ctx.fill();
44385     },
44386     
44387     drawCurve: function (_a) {
44388         var color = _a.color, curve = _a.curve;
44389         var ctx = this.canvasElCtx();
44390         var widthDelta = curve.endWidth - curve.startWidth;
44391         var drawSteps = Math.floor(curve.length()) * 2;
44392         ctx.beginPath();
44393         ctx.fillStyle = color;
44394         for (var i = 0; i < drawSteps; i += 1) {
44395         var t = i / drawSteps;
44396         var tt = t * t;
44397         var ttt = tt * t;
44398         var u = 1 - t;
44399         var uu = u * u;
44400         var uuu = uu * u;
44401         var x = uuu * curve.startPoint.x;
44402         x += 3 * uu * t * curve.control1.x;
44403         x += 3 * u * tt * curve.control2.x;
44404         x += ttt * curve.endPoint.x;
44405         var y = uuu * curve.startPoint.y;
44406         y += 3 * uu * t * curve.control1.y;
44407         y += 3 * u * tt * curve.control2.y;
44408         y += ttt * curve.endPoint.y;
44409         var width = curve.startWidth + ttt * widthDelta;
44410         this.drawCurveSegment(x, y, width);
44411         }
44412         ctx.closePath();
44413         ctx.fill();
44414     },
44415     
44416     drawCurveSegment: function (x, y, width) {
44417         var ctx = this.canvasElCtx();
44418         ctx.moveTo(x, y);
44419         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44420         this.is_empty = false;
44421     },
44422     
44423     clear: function()
44424     {
44425         var ctx = this.canvasElCtx();
44426         var canvas = this.canvasEl().dom;
44427         ctx.fillStyle = this.bg_color;
44428         ctx.clearRect(0, 0, canvas.width, canvas.height);
44429         ctx.fillRect(0, 0, canvas.width, canvas.height);
44430         this.curve_data = [];
44431         this.reset();
44432         this.is_empty = true;
44433     },
44434     
44435     fileEl: function()
44436     {
44437         return  this.el.select('input',true).first();
44438     },
44439     
44440     canvasEl: function()
44441     {
44442         return this.el.select('canvas',true).first();
44443     },
44444     
44445     canvasElCtx: function()
44446     {
44447         return this.el.select('canvas',true).first().dom.getContext('2d');
44448     },
44449     
44450     getImage: function(type)
44451     {
44452         if(this.is_empty) {
44453             return false;
44454         }
44455         
44456         // encryption ?
44457         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44458     },
44459     
44460     drawFromImage: function(img_src)
44461     {
44462         var img = new Image();
44463         
44464         img.onload = function(){
44465             this.canvasElCtx().drawImage(img, 0, 0);
44466         }.bind(this);
44467         
44468         img.src = img_src;
44469         
44470         this.is_empty = false;
44471     },
44472     
44473     selectImage: function()
44474     {
44475         this.fileEl().dom.click();
44476     },
44477     
44478     uploadImage: function(e)
44479     {
44480         var reader = new FileReader();
44481         
44482         reader.onload = function(e){
44483             var img = new Image();
44484             img.onload = function(){
44485                 this.reset();
44486                 this.canvasElCtx().drawImage(img, 0, 0);
44487             }.bind(this);
44488             img.src = e.target.result;
44489         }.bind(this);
44490         
44491         reader.readAsDataURL(e.target.files[0]);
44492     },
44493     
44494     // Bezier Point Constructor
44495     Point: (function () {
44496         function Point(x, y, time) {
44497             this.x = x;
44498             this.y = y;
44499             this.time = time || Date.now();
44500         }
44501         Point.prototype.distanceTo = function (start) {
44502             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44503         };
44504         Point.prototype.equals = function (other) {
44505             return this.x === other.x && this.y === other.y && this.time === other.time;
44506         };
44507         Point.prototype.velocityFrom = function (start) {
44508             return this.time !== start.time
44509             ? this.distanceTo(start) / (this.time - start.time)
44510             : 0;
44511         };
44512         return Point;
44513     }()),
44514     
44515     
44516     // Bezier Constructor
44517     Bezier: (function () {
44518         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44519             this.startPoint = startPoint;
44520             this.control2 = control2;
44521             this.control1 = control1;
44522             this.endPoint = endPoint;
44523             this.startWidth = startWidth;
44524             this.endWidth = endWidth;
44525         }
44526         Bezier.fromPoints = function (points, widths, scope) {
44527             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44528             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44529             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44530         };
44531         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44532             var dx1 = s1.x - s2.x;
44533             var dy1 = s1.y - s2.y;
44534             var dx2 = s2.x - s3.x;
44535             var dy2 = s2.y - s3.y;
44536             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44537             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44538             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44539             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44540             var dxm = m1.x - m2.x;
44541             var dym = m1.y - m2.y;
44542             var k = l2 / (l1 + l2);
44543             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44544             var tx = s2.x - cm.x;
44545             var ty = s2.y - cm.y;
44546             return {
44547                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44548                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44549             };
44550         };
44551         Bezier.prototype.length = function () {
44552             var steps = 10;
44553             var length = 0;
44554             var px;
44555             var py;
44556             for (var i = 0; i <= steps; i += 1) {
44557                 var t = i / steps;
44558                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44559                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44560                 if (i > 0) {
44561                     var xdiff = cx - px;
44562                     var ydiff = cy - py;
44563                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44564                 }
44565                 px = cx;
44566                 py = cy;
44567             }
44568             return length;
44569         };
44570         Bezier.prototype.point = function (t, start, c1, c2, end) {
44571             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44572             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44573             + (3.0 * c2 * (1.0 - t) * t * t)
44574             + (end * t * t * t);
44575         };
44576         return Bezier;
44577     }()),
44578     
44579     throttleStroke: function(fn, wait) {
44580       if (wait === void 0) { wait = 250; }
44581       var previous = 0;
44582       var timeout = null;
44583       var result;
44584       var storedContext;
44585       var storedArgs;
44586       var later = function () {
44587           previous = Date.now();
44588           timeout = null;
44589           result = fn.apply(storedContext, storedArgs);
44590           if (!timeout) {
44591               storedContext = null;
44592               storedArgs = [];
44593           }
44594       };
44595       return function wrapper() {
44596           var args = [];
44597           for (var _i = 0; _i < arguments.length; _i++) {
44598               args[_i] = arguments[_i];
44599           }
44600           var now = Date.now();
44601           var remaining = wait - (now - previous);
44602           storedContext = this;
44603           storedArgs = args;
44604           if (remaining <= 0 || remaining > wait) {
44605               if (timeout) {
44606                   clearTimeout(timeout);
44607                   timeout = null;
44608               }
44609               previous = now;
44610               result = fn.apply(storedContext, storedArgs);
44611               if (!timeout) {
44612                   storedContext = null;
44613                   storedArgs = [];
44614               }
44615           }
44616           else if (!timeout) {
44617               timeout = window.setTimeout(later, remaining);
44618           }
44619           return result;
44620       };
44621   }
44622   
44623 });
44624
44625  
44626
44627