reset upload after call
[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 /*
2853 * Licence: LGPL
2854 */
2855
2856 /**
2857  * @class Roo.bootstrap.ButtonUploader
2858  * @extends Roo.bootstrap.Button
2859  * Bootstrap Button Uploader class - it's a button which when you add files to it
2860  *
2861  * 
2862  * @cfg {Number} errorTimeout default 3000
2863  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2864  * @cfg {Array}  html The button text.
2865
2866  *
2867  * @constructor
2868  * Create a new CardUploader
2869  * @param {Object} config The config object
2870  */
2871
2872 Roo.bootstrap.ButtonUploader = function(config){
2873     
2874  
2875     
2876     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2877     
2878      
2879      this.addEvents({
2880          // raw events
2881         /**
2882          * @event beforeselect
2883          * When button is pressed, before show upload files dialog is shown
2884          * @param {Roo.bootstrap.UploaderButton} this
2885          *
2886          */
2887         'beforeselect' : true,
2888          /**
2889          * @event fired when files have been selected, 
2890          * When a the download link is clicked
2891          * @param {Roo.bootstrap.UploaderButton} this
2892          * @param {Array} Array of files that have been uploaded
2893          */
2894         'uploaded' : true
2895         
2896     });
2897 };
2898  
2899 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2900     
2901      
2902     errorTimeout : 3000,
2903      
2904     images : false,
2905    
2906     fileCollection : false,
2907     allowBlank : true,
2908     
2909     getAutoCreate : function()
2910     {
2911         
2912         
2913         return  {
2914             cls :'div' ,
2915             cn : [
2916                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2917                 {
2918                     tag: 'input',
2919                     multiple : 'multiple',
2920                     type : 'file',
2921                     cls : 'd-none  roo-card-upload-selector' 
2922                   
2923                 }
2924                  
2925
2926             ]
2927         };
2928            
2929          
2930     },
2931      
2932    
2933     initEvents : function()
2934     {
2935         
2936         Roo.bootstrap.Button.prototype.initEvents.call(this);
2937         
2938         
2939         
2940         
2941         
2942         this.urlAPI = (window.createObjectURL && window) || 
2943                                 (window.URL && URL.revokeObjectURL && URL) || 
2944                                 (window.webkitURL && webkitURL);
2945                         
2946          
2947          
2948          
2949         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2950         
2951         this.selectorEl.on('change', this.onFileSelected, this);
2952          
2953          
2954        
2955     },
2956     
2957    
2958     onClick : function(e)
2959     {
2960         e.preventDefault();
2961         
2962         if ( this.fireEvent('beforeselect', this) === false) {
2963             return;
2964         }
2965          
2966         this.selectorEl.dom.click();
2967          
2968     },
2969     
2970     onFileSelected : function(e)
2971     {
2972         e.preventDefault();
2973         
2974         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2975             return;
2976         }
2977         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2978         this.selectorEl.dom.reset();
2979         
2980         this.fireEvent('uploaded', this,  files );
2981         
2982     },
2983     
2984        
2985    
2986     
2987     /**
2988      * addCard - add an Attachment to the uploader
2989      * @param data - the data about the image to upload
2990      *
2991      * {
2992           id : 123
2993           title : "Title of file",
2994           is_uploaded : false,
2995           src : "http://.....",
2996           srcfile : { the File upload object },
2997           mimetype : file.type,
2998           preview : false,
2999           is_deleted : 0
3000           .. any other data...
3001         }
3002      *
3003      * 
3004     */
3005      
3006     reset: function()
3007     {
3008          
3009          this.selectorEl
3010     } 
3011     
3012     
3013     
3014     
3015 });
3016  /*
3017  * - LGPL
3018  *
3019  * image
3020  * 
3021  */
3022
3023
3024 /**
3025  * @class Roo.bootstrap.Img
3026  * @extends Roo.bootstrap.Component
3027  * Bootstrap Img class
3028  * @cfg {Boolean} imgResponsive false | true
3029  * @cfg {String} border rounded | circle | thumbnail
3030  * @cfg {String} src image source
3031  * @cfg {String} alt image alternative text
3032  * @cfg {String} href a tag href
3033  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3034  * @cfg {String} xsUrl xs image source
3035  * @cfg {String} smUrl sm image source
3036  * @cfg {String} mdUrl md image source
3037  * @cfg {String} lgUrl lg image source
3038  * 
3039  * @constructor
3040  * Create a new Input
3041  * @param {Object} config The config object
3042  */
3043
3044 Roo.bootstrap.Img = function(config){
3045     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3046     
3047     this.addEvents({
3048         // img events
3049         /**
3050          * @event click
3051          * The img click event for the img.
3052          * @param {Roo.EventObject} e
3053          */
3054         "click" : true
3055     });
3056 };
3057
3058 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3059     
3060     imgResponsive: true,
3061     border: '',
3062     src: 'about:blank',
3063     href: false,
3064     target: false,
3065     xsUrl: '',
3066     smUrl: '',
3067     mdUrl: '',
3068     lgUrl: '',
3069
3070     getAutoCreate : function()
3071     {   
3072         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3073             return this.createSingleImg();
3074         }
3075         
3076         var cfg = {
3077             tag: 'div',
3078             cls: 'roo-image-responsive-group',
3079             cn: []
3080         };
3081         var _this = this;
3082         
3083         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3084             
3085             if(!_this[size + 'Url']){
3086                 return;
3087             }
3088             
3089             var img = {
3090                 tag: 'img',
3091                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3092                 html: _this.html || cfg.html,
3093                 src: _this[size + 'Url']
3094             };
3095             
3096             img.cls += ' roo-image-responsive-' + size;
3097             
3098             var s = ['xs', 'sm', 'md', 'lg'];
3099             
3100             s.splice(s.indexOf(size), 1);
3101             
3102             Roo.each(s, function(ss){
3103                 img.cls += ' hidden-' + ss;
3104             });
3105             
3106             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3107                 cfg.cls += ' img-' + _this.border;
3108             }
3109             
3110             if(_this.alt){
3111                 cfg.alt = _this.alt;
3112             }
3113             
3114             if(_this.href){
3115                 var a = {
3116                     tag: 'a',
3117                     href: _this.href,
3118                     cn: [
3119                         img
3120                     ]
3121                 };
3122
3123                 if(this.target){
3124                     a.target = _this.target;
3125                 }
3126             }
3127             
3128             cfg.cn.push((_this.href) ? a : img);
3129             
3130         });
3131         
3132         return cfg;
3133     },
3134     
3135     createSingleImg : function()
3136     {
3137         var cfg = {
3138             tag: 'img',
3139             cls: (this.imgResponsive) ? 'img-responsive' : '',
3140             html : null,
3141             src : 'about:blank'  // just incase src get's set to undefined?!?
3142         };
3143         
3144         cfg.html = this.html || cfg.html;
3145         
3146         cfg.src = this.src || cfg.src;
3147         
3148         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3149             cfg.cls += ' img-' + this.border;
3150         }
3151         
3152         if(this.alt){
3153             cfg.alt = this.alt;
3154         }
3155         
3156         if(this.href){
3157             var a = {
3158                 tag: 'a',
3159                 href: this.href,
3160                 cn: [
3161                     cfg
3162                 ]
3163             };
3164             
3165             if(this.target){
3166                 a.target = this.target;
3167             }
3168             
3169         }
3170         
3171         return (this.href) ? a : cfg;
3172     },
3173     
3174     initEvents: function() 
3175     {
3176         if(!this.href){
3177             this.el.on('click', this.onClick, this);
3178         }
3179         
3180     },
3181     
3182     onClick : function(e)
3183     {
3184         Roo.log('img onclick');
3185         this.fireEvent('click', this, e);
3186     },
3187     /**
3188      * Sets the url of the image - used to update it
3189      * @param {String} url the url of the image
3190      */
3191     
3192     setSrc : function(url)
3193     {
3194         this.src =  url;
3195         
3196         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3197             this.el.dom.src =  url;
3198             return;
3199         }
3200         
3201         this.el.select('img', true).first().dom.src =  url;
3202     }
3203     
3204     
3205    
3206 });
3207
3208  /*
3209  * - LGPL
3210  *
3211  * image
3212  * 
3213  */
3214
3215
3216 /**
3217  * @class Roo.bootstrap.Link
3218  * @extends Roo.bootstrap.Component
3219  * Bootstrap Link Class
3220  * @cfg {String} alt image alternative text
3221  * @cfg {String} href a tag href
3222  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3223  * @cfg {String} html the content of the link.
3224  * @cfg {String} anchor name for the anchor link
3225  * @cfg {String} fa - favicon
3226
3227  * @cfg {Boolean} preventDefault (true | false) default false
3228
3229  * 
3230  * @constructor
3231  * Create a new Input
3232  * @param {Object} config The config object
3233  */
3234
3235 Roo.bootstrap.Link = function(config){
3236     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3237     
3238     this.addEvents({
3239         // img events
3240         /**
3241          * @event click
3242          * The img click event for the img.
3243          * @param {Roo.EventObject} e
3244          */
3245         "click" : true
3246     });
3247 };
3248
3249 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3250     
3251     href: false,
3252     target: false,
3253     preventDefault: false,
3254     anchor : false,
3255     alt : false,
3256     fa: false,
3257
3258
3259     getAutoCreate : function()
3260     {
3261         var html = this.html || '';
3262         
3263         if (this.fa !== false) {
3264             html = '<i class="fa fa-' + this.fa + '"></i>';
3265         }
3266         var cfg = {
3267             tag: 'a'
3268         };
3269         // anchor's do not require html/href...
3270         if (this.anchor === false) {
3271             cfg.html = html;
3272             cfg.href = this.href || '#';
3273         } else {
3274             cfg.name = this.anchor;
3275             if (this.html !== false || this.fa !== false) {
3276                 cfg.html = html;
3277             }
3278             if (this.href !== false) {
3279                 cfg.href = this.href;
3280             }
3281         }
3282         
3283         if(this.alt !== false){
3284             cfg.alt = this.alt;
3285         }
3286         
3287         
3288         if(this.target !== false) {
3289             cfg.target = this.target;
3290         }
3291         
3292         return cfg;
3293     },
3294     
3295     initEvents: function() {
3296         
3297         if(!this.href || this.preventDefault){
3298             this.el.on('click', this.onClick, this);
3299         }
3300     },
3301     
3302     onClick : function(e)
3303     {
3304         if(this.preventDefault){
3305             e.preventDefault();
3306         }
3307         //Roo.log('img onclick');
3308         this.fireEvent('click', this, e);
3309     }
3310    
3311 });
3312
3313  /*
3314  * - LGPL
3315  *
3316  * header
3317  * 
3318  */
3319
3320 /**
3321  * @class Roo.bootstrap.Header
3322  * @extends Roo.bootstrap.Component
3323  * Bootstrap Header class
3324  * @cfg {String} html content of header
3325  * @cfg {Number} level (1|2|3|4|5|6) default 1
3326  * 
3327  * @constructor
3328  * Create a new Header
3329  * @param {Object} config The config object
3330  */
3331
3332
3333 Roo.bootstrap.Header  = function(config){
3334     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3335 };
3336
3337 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3338     
3339     //href : false,
3340     html : false,
3341     level : 1,
3342     
3343     
3344     
3345     getAutoCreate : function(){
3346         
3347         
3348         
3349         var cfg = {
3350             tag: 'h' + (1 *this.level),
3351             html: this.html || ''
3352         } ;
3353         
3354         return cfg;
3355     }
3356    
3357 });
3358
3359  
3360
3361  /*
3362  * Based on:
3363  * Ext JS Library 1.1.1
3364  * Copyright(c) 2006-2007, Ext JS, LLC.
3365  *
3366  * Originally Released Under LGPL - original licence link has changed is not relivant.
3367  *
3368  * Fork - LGPL
3369  * <script type="text/javascript">
3370  */
3371  
3372 /**
3373  * @class Roo.bootstrap.MenuMgr
3374  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3375  * @singleton
3376  */
3377 Roo.bootstrap.MenuMgr = function(){
3378    var menus, active, groups = {}, attached = false, lastShow = new Date();
3379
3380    // private - called when first menu is created
3381    function init(){
3382        menus = {};
3383        active = new Roo.util.MixedCollection();
3384        Roo.get(document).addKeyListener(27, function(){
3385            if(active.length > 0){
3386                hideAll();
3387            }
3388        });
3389    }
3390
3391    // private
3392    function hideAll(){
3393        if(active && active.length > 0){
3394            var c = active.clone();
3395            c.each(function(m){
3396                m.hide();
3397            });
3398        }
3399    }
3400
3401    // private
3402    function onHide(m){
3403        active.remove(m);
3404        if(active.length < 1){
3405            Roo.get(document).un("mouseup", onMouseDown);
3406             
3407            attached = false;
3408        }
3409    }
3410
3411    // private
3412    function onShow(m){
3413        var last = active.last();
3414        lastShow = new Date();
3415        active.add(m);
3416        if(!attached){
3417           Roo.get(document).on("mouseup", onMouseDown);
3418            
3419            attached = true;
3420        }
3421        if(m.parentMenu){
3422           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3423           m.parentMenu.activeChild = m;
3424        }else if(last && last.isVisible()){
3425           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3426        }
3427    }
3428
3429    // private
3430    function onBeforeHide(m){
3431        if(m.activeChild){
3432            m.activeChild.hide();
3433        }
3434        if(m.autoHideTimer){
3435            clearTimeout(m.autoHideTimer);
3436            delete m.autoHideTimer;
3437        }
3438    }
3439
3440    // private
3441    function onBeforeShow(m){
3442        var pm = m.parentMenu;
3443        if(!pm && !m.allowOtherMenus){
3444            hideAll();
3445        }else if(pm && pm.activeChild && active != m){
3446            pm.activeChild.hide();
3447        }
3448    }
3449
3450    // private this should really trigger on mouseup..
3451    function onMouseDown(e){
3452         Roo.log("on Mouse Up");
3453         
3454         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3455             Roo.log("MenuManager hideAll");
3456             hideAll();
3457             e.stopEvent();
3458         }
3459         
3460         
3461    }
3462
3463    // private
3464    function onBeforeCheck(mi, state){
3465        if(state){
3466            var g = groups[mi.group];
3467            for(var i = 0, l = g.length; i < l; i++){
3468                if(g[i] != mi){
3469                    g[i].setChecked(false);
3470                }
3471            }
3472        }
3473    }
3474
3475    return {
3476
3477        /**
3478         * Hides all menus that are currently visible
3479         */
3480        hideAll : function(){
3481             hideAll();  
3482        },
3483
3484        // private
3485        register : function(menu){
3486            if(!menus){
3487                init();
3488            }
3489            menus[menu.id] = menu;
3490            menu.on("beforehide", onBeforeHide);
3491            menu.on("hide", onHide);
3492            menu.on("beforeshow", onBeforeShow);
3493            menu.on("show", onShow);
3494            var g = menu.group;
3495            if(g && menu.events["checkchange"]){
3496                if(!groups[g]){
3497                    groups[g] = [];
3498                }
3499                groups[g].push(menu);
3500                menu.on("checkchange", onCheck);
3501            }
3502        },
3503
3504         /**
3505          * Returns a {@link Roo.menu.Menu} object
3506          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3507          * be used to generate and return a new Menu instance.
3508          */
3509        get : function(menu){
3510            if(typeof menu == "string"){ // menu id
3511                return menus[menu];
3512            }else if(menu.events){  // menu instance
3513                return menu;
3514            }
3515            /*else if(typeof menu.length == 'number'){ // array of menu items?
3516                return new Roo.bootstrap.Menu({items:menu});
3517            }else{ // otherwise, must be a config
3518                return new Roo.bootstrap.Menu(menu);
3519            }
3520            */
3521            return false;
3522        },
3523
3524        // private
3525        unregister : function(menu){
3526            delete menus[menu.id];
3527            menu.un("beforehide", onBeforeHide);
3528            menu.un("hide", onHide);
3529            menu.un("beforeshow", onBeforeShow);
3530            menu.un("show", onShow);
3531            var g = menu.group;
3532            if(g && menu.events["checkchange"]){
3533                groups[g].remove(menu);
3534                menu.un("checkchange", onCheck);
3535            }
3536        },
3537
3538        // private
3539        registerCheckable : function(menuItem){
3540            var g = menuItem.group;
3541            if(g){
3542                if(!groups[g]){
3543                    groups[g] = [];
3544                }
3545                groups[g].push(menuItem);
3546                menuItem.on("beforecheckchange", onBeforeCheck);
3547            }
3548        },
3549
3550        // private
3551        unregisterCheckable : function(menuItem){
3552            var g = menuItem.group;
3553            if(g){
3554                groups[g].remove(menuItem);
3555                menuItem.un("beforecheckchange", onBeforeCheck);
3556            }
3557        }
3558    };
3559 }();/*
3560  * - LGPL
3561  *
3562  * menu
3563  * 
3564  */
3565
3566 /**
3567  * @class Roo.bootstrap.Menu
3568  * @extends Roo.bootstrap.Component
3569  * Bootstrap Menu class - container for MenuItems
3570  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3571  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3572  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3573  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3574  * 
3575  * @constructor
3576  * Create a new Menu
3577  * @param {Object} config The config object
3578  */
3579
3580
3581 Roo.bootstrap.Menu = function(config){
3582     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3583     if (this.registerMenu && this.type != 'treeview')  {
3584         Roo.bootstrap.MenuMgr.register(this);
3585     }
3586     
3587     
3588     this.addEvents({
3589         /**
3590          * @event beforeshow
3591          * Fires before this menu is displayed (return false to block)
3592          * @param {Roo.menu.Menu} this
3593          */
3594         beforeshow : true,
3595         /**
3596          * @event beforehide
3597          * Fires before this menu is hidden (return false to block)
3598          * @param {Roo.menu.Menu} this
3599          */
3600         beforehide : true,
3601         /**
3602          * @event show
3603          * Fires after this menu is displayed
3604          * @param {Roo.menu.Menu} this
3605          */
3606         show : true,
3607         /**
3608          * @event hide
3609          * Fires after this menu is hidden
3610          * @param {Roo.menu.Menu} this
3611          */
3612         hide : true,
3613         /**
3614          * @event click
3615          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3616          * @param {Roo.menu.Menu} this
3617          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3618          * @param {Roo.EventObject} e
3619          */
3620         click : true,
3621         /**
3622          * @event mouseover
3623          * Fires when the mouse is hovering over this menu
3624          * @param {Roo.menu.Menu} this
3625          * @param {Roo.EventObject} e
3626          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3627          */
3628         mouseover : true,
3629         /**
3630          * @event mouseout
3631          * Fires when the mouse exits this menu
3632          * @param {Roo.menu.Menu} this
3633          * @param {Roo.EventObject} e
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          */
3636         mouseout : true,
3637         /**
3638          * @event itemclick
3639          * Fires when a menu item contained in this menu is clicked
3640          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3641          * @param {Roo.EventObject} e
3642          */
3643         itemclick: true
3644     });
3645     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3646 };
3647
3648 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3649     
3650    /// html : false,
3651     //align : '',
3652     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3653     type: false,
3654     /**
3655      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3656      */
3657     registerMenu : true,
3658     
3659     menuItems :false, // stores the menu items..
3660     
3661     hidden:true,
3662         
3663     parentMenu : false,
3664     
3665     stopEvent : true,
3666     
3667     isLink : false,
3668     
3669     getChildContainer : function() {
3670         return this.el;  
3671     },
3672     
3673     getAutoCreate : function(){
3674          
3675         //if (['right'].indexOf(this.align)!==-1) {
3676         //    cfg.cn[1].cls += ' pull-right'
3677         //}
3678         
3679         
3680         var cfg = {
3681             tag : 'ul',
3682             cls : 'dropdown-menu' ,
3683             style : 'z-index:1000'
3684             
3685         };
3686         
3687         if (this.type === 'submenu') {
3688             cfg.cls = 'submenu active';
3689         }
3690         if (this.type === 'treeview') {
3691             cfg.cls = 'treeview-menu';
3692         }
3693         
3694         return cfg;
3695     },
3696     initEvents : function() {
3697         
3698        // Roo.log("ADD event");
3699        // Roo.log(this.triggerEl.dom);
3700         
3701         this.triggerEl.on('click', this.onTriggerClick, this);
3702         
3703         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3704         
3705         
3706         if (this.triggerEl.hasClass('nav-item')) {
3707             // dropdown toggle on the 'a' in BS4?
3708             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3709         } else {
3710             this.triggerEl.addClass('dropdown-toggle');
3711         }
3712         if (Roo.isTouch) {
3713             this.el.on('touchstart'  , this.onTouch, this);
3714         }
3715         this.el.on('click' , this.onClick, this);
3716
3717         this.el.on("mouseover", this.onMouseOver, this);
3718         this.el.on("mouseout", this.onMouseOut, this);
3719         
3720     },
3721     
3722     findTargetItem : function(e)
3723     {
3724         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3725         if(!t){
3726             return false;
3727         }
3728         //Roo.log(t);         Roo.log(t.id);
3729         if(t && t.id){
3730             //Roo.log(this.menuitems);
3731             return this.menuitems.get(t.id);
3732             
3733             //return this.items.get(t.menuItemId);
3734         }
3735         
3736         return false;
3737     },
3738     
3739     onTouch : function(e) 
3740     {
3741         Roo.log("menu.onTouch");
3742         //e.stopEvent(); this make the user popdown broken
3743         this.onClick(e);
3744     },
3745     
3746     onClick : function(e)
3747     {
3748         Roo.log("menu.onClick");
3749         
3750         var t = this.findTargetItem(e);
3751         if(!t || t.isContainer){
3752             return;
3753         }
3754         Roo.log(e);
3755         /*
3756         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3757             if(t == this.activeItem && t.shouldDeactivate(e)){
3758                 this.activeItem.deactivate();
3759                 delete this.activeItem;
3760                 return;
3761             }
3762             if(t.canActivate){
3763                 this.setActiveItem(t, true);
3764             }
3765             return;
3766             
3767             
3768         }
3769         */
3770        
3771         Roo.log('pass click event');
3772         
3773         t.onClick(e);
3774         
3775         this.fireEvent("click", this, t, e);
3776         
3777         var _this = this;
3778         
3779         if(!t.href.length || t.href == '#'){
3780             (function() { _this.hide(); }).defer(100);
3781         }
3782         
3783     },
3784     
3785     onMouseOver : function(e){
3786         var t  = this.findTargetItem(e);
3787         //Roo.log(t);
3788         //if(t){
3789         //    if(t.canActivate && !t.disabled){
3790         //        this.setActiveItem(t, true);
3791         //    }
3792         //}
3793         
3794         this.fireEvent("mouseover", this, e, t);
3795     },
3796     isVisible : function(){
3797         return !this.hidden;
3798     },
3799     onMouseOut : function(e){
3800         var t  = this.findTargetItem(e);
3801         
3802         //if(t ){
3803         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3804         //        this.activeItem.deactivate();
3805         //        delete this.activeItem;
3806         //    }
3807         //}
3808         this.fireEvent("mouseout", this, e, t);
3809     },
3810     
3811     
3812     /**
3813      * Displays this menu relative to another element
3814      * @param {String/HTMLElement/Roo.Element} element The element to align to
3815      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3816      * the element (defaults to this.defaultAlign)
3817      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3818      */
3819     show : function(el, pos, parentMenu)
3820     {
3821         if (false === this.fireEvent("beforeshow", this)) {
3822             Roo.log("show canceled");
3823             return;
3824         }
3825         this.parentMenu = parentMenu;
3826         if(!this.el){
3827             this.render();
3828         }
3829         
3830         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3831     },
3832      /**
3833      * Displays this menu at a specific xy position
3834      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3835      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3836      */
3837     showAt : function(xy, parentMenu, /* private: */_e){
3838         this.parentMenu = parentMenu;
3839         if(!this.el){
3840             this.render();
3841         }
3842         if(_e !== false){
3843             this.fireEvent("beforeshow", this);
3844             //xy = this.el.adjustForConstraints(xy);
3845         }
3846         
3847         //this.el.show();
3848         this.hideMenuItems();
3849         this.hidden = false;
3850         this.triggerEl.addClass('open');
3851         this.el.addClass('show');
3852         
3853         // reassign x when hitting right
3854         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3855             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3856         }
3857         
3858         // reassign y when hitting bottom
3859         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3860             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3861         }
3862         
3863         // but the list may align on trigger left or trigger top... should it be a properity?
3864         
3865         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3866             this.el.setXY(xy);
3867         }
3868         
3869         this.focus();
3870         this.fireEvent("show", this);
3871     },
3872     
3873     focus : function(){
3874         return;
3875         if(!this.hidden){
3876             this.doFocus.defer(50, this);
3877         }
3878     },
3879
3880     doFocus : function(){
3881         if(!this.hidden){
3882             this.focusEl.focus();
3883         }
3884     },
3885
3886     /**
3887      * Hides this menu and optionally all parent menus
3888      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3889      */
3890     hide : function(deep)
3891     {
3892         if (false === this.fireEvent("beforehide", this)) {
3893             Roo.log("hide canceled");
3894             return;
3895         }
3896         this.hideMenuItems();
3897         if(this.el && this.isVisible()){
3898            
3899             if(this.activeItem){
3900                 this.activeItem.deactivate();
3901                 this.activeItem = null;
3902             }
3903             this.triggerEl.removeClass('open');;
3904             this.el.removeClass('show');
3905             this.hidden = true;
3906             this.fireEvent("hide", this);
3907         }
3908         if(deep === true && this.parentMenu){
3909             this.parentMenu.hide(true);
3910         }
3911     },
3912     
3913     onTriggerClick : function(e)
3914     {
3915         Roo.log('trigger click');
3916         
3917         var target = e.getTarget();
3918         
3919         Roo.log(target.nodeName.toLowerCase());
3920         
3921         if(target.nodeName.toLowerCase() === 'i'){
3922             e.preventDefault();
3923         }
3924         
3925     },
3926     
3927     onTriggerPress  : function(e)
3928     {
3929         Roo.log('trigger press');
3930         //Roo.log(e.getTarget());
3931        // Roo.log(this.triggerEl.dom);
3932        
3933         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3934         var pel = Roo.get(e.getTarget());
3935         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3936             Roo.log('is treeview or dropdown?');
3937             return;
3938         }
3939         
3940         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3941             return;
3942         }
3943         
3944         if (this.isVisible()) {
3945             Roo.log('hide');
3946             this.hide();
3947         } else {
3948             Roo.log('show');
3949             this.show(this.triggerEl, '?', false);
3950         }
3951         
3952         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3953             e.stopEvent();
3954         }
3955         
3956     },
3957        
3958     
3959     hideMenuItems : function()
3960     {
3961         Roo.log("hide Menu Items");
3962         if (!this.el) { 
3963             return;
3964         }
3965         
3966         this.el.select('.open',true).each(function(aa) {
3967             
3968             aa.removeClass('open');
3969          
3970         });
3971     },
3972     addxtypeChild : function (tree, cntr) {
3973         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3974           
3975         this.menuitems.add(comp);
3976         return comp;
3977
3978     },
3979     getEl : function()
3980     {
3981         Roo.log(this.el);
3982         return this.el;
3983     },
3984     
3985     clear : function()
3986     {
3987         this.getEl().dom.innerHTML = '';
3988         this.menuitems.clear();
3989     }
3990 });
3991
3992  
3993  /*
3994  * - LGPL
3995  *
3996  * menu item
3997  * 
3998  */
3999
4000
4001 /**
4002  * @class Roo.bootstrap.MenuItem
4003  * @extends Roo.bootstrap.Component
4004  * Bootstrap MenuItem class
4005  * @cfg {String} html the menu label
4006  * @cfg {String} href the link
4007  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4008  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4009  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4010  * @cfg {String} fa favicon to show on left of menu item.
4011  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4012  * 
4013  * 
4014  * @constructor
4015  * Create a new MenuItem
4016  * @param {Object} config The config object
4017  */
4018
4019
4020 Roo.bootstrap.MenuItem = function(config){
4021     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4022     this.addEvents({
4023         // raw events
4024         /**
4025          * @event click
4026          * The raw click event for the entire grid.
4027          * @param {Roo.bootstrap.MenuItem} this
4028          * @param {Roo.EventObject} e
4029          */
4030         "click" : true
4031     });
4032 };
4033
4034 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4035     
4036     href : false,
4037     html : false,
4038     preventDefault: false,
4039     isContainer : false,
4040     active : false,
4041     fa: false,
4042     
4043     getAutoCreate : function(){
4044         
4045         if(this.isContainer){
4046             return {
4047                 tag: 'li',
4048                 cls: 'dropdown-menu-item '
4049             };
4050         }
4051         var ctag = {
4052             tag: 'span',
4053             html: 'Link'
4054         };
4055         
4056         var anc = {
4057             tag : 'a',
4058             cls : 'dropdown-item',
4059             href : '#',
4060             cn : [  ]
4061         };
4062         
4063         if (this.fa !== false) {
4064             anc.cn.push({
4065                 tag : 'i',
4066                 cls : 'fa fa-' + this.fa
4067             });
4068         }
4069         
4070         anc.cn.push(ctag);
4071         
4072         
4073         var cfg= {
4074             tag: 'li',
4075             cls: 'dropdown-menu-item',
4076             cn: [ anc ]
4077         };
4078         if (this.parent().type == 'treeview') {
4079             cfg.cls = 'treeview-menu';
4080         }
4081         if (this.active) {
4082             cfg.cls += ' active';
4083         }
4084         
4085         
4086         
4087         anc.href = this.href || cfg.cn[0].href ;
4088         ctag.html = this.html || cfg.cn[0].html ;
4089         return cfg;
4090     },
4091     
4092     initEvents: function()
4093     {
4094         if (this.parent().type == 'treeview') {
4095             this.el.select('a').on('click', this.onClick, this);
4096         }
4097         
4098         if (this.menu) {
4099             this.menu.parentType = this.xtype;
4100             this.menu.triggerEl = this.el;
4101             this.menu = this.addxtype(Roo.apply({}, this.menu));
4102         }
4103         
4104     },
4105     onClick : function(e)
4106     {
4107         Roo.log('item on click ');
4108         
4109         if(this.preventDefault){
4110             e.preventDefault();
4111         }
4112         //this.parent().hideMenuItems();
4113         
4114         this.fireEvent('click', this, e);
4115     },
4116     getEl : function()
4117     {
4118         return this.el;
4119     } 
4120 });
4121
4122  
4123
4124  /*
4125  * - LGPL
4126  *
4127  * menu separator
4128  * 
4129  */
4130
4131
4132 /**
4133  * @class Roo.bootstrap.MenuSeparator
4134  * @extends Roo.bootstrap.Component
4135  * Bootstrap MenuSeparator class
4136  * 
4137  * @constructor
4138  * Create a new MenuItem
4139  * @param {Object} config The config object
4140  */
4141
4142
4143 Roo.bootstrap.MenuSeparator = function(config){
4144     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4145 };
4146
4147 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4148     
4149     getAutoCreate : function(){
4150         var cfg = {
4151             cls: 'divider',
4152             tag : 'li'
4153         };
4154         
4155         return cfg;
4156     }
4157    
4158 });
4159
4160  
4161
4162  
4163 /*
4164 * Licence: LGPL
4165 */
4166
4167 /**
4168  * @class Roo.bootstrap.Modal
4169  * @extends Roo.bootstrap.Component
4170  * Bootstrap Modal class
4171  * @cfg {String} title Title of dialog
4172  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4173  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4174  * @cfg {Boolean} specificTitle default false
4175  * @cfg {Array} buttons Array of buttons or standard button set..
4176  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4177  * @cfg {Boolean} animate default true
4178  * @cfg {Boolean} allow_close default true
4179  * @cfg {Boolean} fitwindow default false
4180  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4181  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4182  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4183  * @cfg {String} size (sm|lg|xl) default empty
4184  * @cfg {Number} max_width set the max width of modal
4185  * @cfg {Boolean} editableTitle can the title be edited
4186
4187  *
4188  *
4189  * @constructor
4190  * Create a new Modal Dialog
4191  * @param {Object} config The config object
4192  */
4193
4194 Roo.bootstrap.Modal = function(config){
4195     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4196     this.addEvents({
4197         // raw events
4198         /**
4199          * @event btnclick
4200          * The raw btnclick event for the button
4201          * @param {Roo.EventObject} e
4202          */
4203         "btnclick" : true,
4204         /**
4205          * @event resize
4206          * Fire when dialog resize
4207          * @param {Roo.bootstrap.Modal} this
4208          * @param {Roo.EventObject} e
4209          */
4210         "resize" : true,
4211         /**
4212          * @event titlechanged
4213          * Fire when the editable title has been changed
4214          * @param {Roo.bootstrap.Modal} this
4215          * @param {Roo.EventObject} value
4216          */
4217         "titlechanged" : true 
4218         
4219     });
4220     this.buttons = this.buttons || [];
4221
4222     if (this.tmpl) {
4223         this.tmpl = Roo.factory(this.tmpl);
4224     }
4225
4226 };
4227
4228 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4229
4230     title : 'test dialog',
4231
4232     buttons : false,
4233
4234     // set on load...
4235
4236     html: false,
4237
4238     tmp: false,
4239
4240     specificTitle: false,
4241
4242     buttonPosition: 'right',
4243
4244     allow_close : true,
4245
4246     animate : true,
4247
4248     fitwindow: false,
4249     
4250      // private
4251     dialogEl: false,
4252     bodyEl:  false,
4253     footerEl:  false,
4254     titleEl:  false,
4255     closeEl:  false,
4256
4257     size: '',
4258     
4259     max_width: 0,
4260     
4261     max_height: 0,
4262     
4263     fit_content: false,
4264     editableTitle  : false,
4265
4266     onRender : function(ct, position)
4267     {
4268         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4269
4270         if(!this.el){
4271             var cfg = Roo.apply({},  this.getAutoCreate());
4272             cfg.id = Roo.id();
4273             //if(!cfg.name){
4274             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4275             //}
4276             //if (!cfg.name.length) {
4277             //    delete cfg.name;
4278            // }
4279             if (this.cls) {
4280                 cfg.cls += ' ' + this.cls;
4281             }
4282             if (this.style) {
4283                 cfg.style = this.style;
4284             }
4285             this.el = Roo.get(document.body).createChild(cfg, position);
4286         }
4287         //var type = this.el.dom.type;
4288
4289
4290         if(this.tabIndex !== undefined){
4291             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4292         }
4293
4294         this.dialogEl = this.el.select('.modal-dialog',true).first();
4295         this.bodyEl = this.el.select('.modal-body',true).first();
4296         this.closeEl = this.el.select('.modal-header .close', true).first();
4297         this.headerEl = this.el.select('.modal-header',true).first();
4298         this.titleEl = this.el.select('.modal-title',true).first();
4299         this.footerEl = this.el.select('.modal-footer',true).first();
4300
4301         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4302         
4303         //this.el.addClass("x-dlg-modal");
4304
4305         if (this.buttons.length) {
4306             Roo.each(this.buttons, function(bb) {
4307                 var b = Roo.apply({}, bb);
4308                 b.xns = b.xns || Roo.bootstrap;
4309                 b.xtype = b.xtype || 'Button';
4310                 if (typeof(b.listeners) == 'undefined') {
4311                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4312                 }
4313
4314                 var btn = Roo.factory(b);
4315
4316                 btn.render(this.getButtonContainer());
4317
4318             },this);
4319         }
4320         // render the children.
4321         var nitems = [];
4322
4323         if(typeof(this.items) != 'undefined'){
4324             var items = this.items;
4325             delete this.items;
4326
4327             for(var i =0;i < items.length;i++) {
4328                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4329             }
4330         }
4331
4332         this.items = nitems;
4333
4334         // where are these used - they used to be body/close/footer
4335
4336
4337         this.initEvents();
4338         //this.el.addClass([this.fieldClass, this.cls]);
4339
4340     },
4341
4342     getAutoCreate : function()
4343     {
4344         // we will default to modal-body-overflow - might need to remove or make optional later.
4345         var bdy = {
4346                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4347                 html : this.html || ''
4348         };
4349
4350         var title = {
4351             tag: 'h5',
4352             cls : 'modal-title',
4353             html : this.title
4354         };
4355
4356         if(this.specificTitle){ // WTF is this?
4357             title = this.title;
4358         }
4359
4360         var header = [];
4361         if (this.allow_close && Roo.bootstrap.version == 3) {
4362             header.push({
4363                 tag: 'button',
4364                 cls : 'close',
4365                 html : '&times'
4366             });
4367         }
4368
4369         header.push(title);
4370
4371         if (this.editableTitle) {
4372             header.push({
4373                 cls: 'form-control roo-editable-title d-none',
4374                 tag: 'input',
4375                 type: 'text'
4376             });
4377         }
4378         
4379         if (this.allow_close && Roo.bootstrap.version == 4) {
4380             header.push({
4381                 tag: 'button',
4382                 cls : 'close',
4383                 html : '&times'
4384             });
4385         }
4386         
4387         var size = '';
4388
4389         if(this.size.length){
4390             size = 'modal-' + this.size;
4391         }
4392         
4393         var footer = Roo.bootstrap.version == 3 ?
4394             {
4395                 cls : 'modal-footer',
4396                 cn : [
4397                     {
4398                         tag: 'div',
4399                         cls: 'btn-' + this.buttonPosition
4400                     }
4401                 ]
4402
4403             } :
4404             {  // BS4 uses mr-auto on left buttons....
4405                 cls : 'modal-footer'
4406             };
4407
4408             
4409
4410         
4411         
4412         var modal = {
4413             cls: "modal",
4414              cn : [
4415                 {
4416                     cls: "modal-dialog " + size,
4417                     cn : [
4418                         {
4419                             cls : "modal-content",
4420                             cn : [
4421                                 {
4422                                     cls : 'modal-header',
4423                                     cn : header
4424                                 },
4425                                 bdy,
4426                                 footer
4427                             ]
4428
4429                         }
4430                     ]
4431
4432                 }
4433             ]
4434         };
4435
4436         if(this.animate){
4437             modal.cls += ' fade';
4438         }
4439
4440         return modal;
4441
4442     },
4443     getChildContainer : function() {
4444
4445          return this.bodyEl;
4446
4447     },
4448     getButtonContainer : function() {
4449         
4450          return Roo.bootstrap.version == 4 ?
4451             this.el.select('.modal-footer',true).first()
4452             : this.el.select('.modal-footer div',true).first();
4453
4454     },
4455     initEvents : function()
4456     {
4457         if (this.allow_close) {
4458             this.closeEl.on('click', this.hide, this);
4459         }
4460         Roo.EventManager.onWindowResize(this.resize, this, true);
4461         if (this.editableTitle) {
4462             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4463             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4464             this.headerEditEl.on('keyup', function(e) {
4465                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4466                         this.toggleHeaderInput(false)
4467                     }
4468                 }, this);
4469             this.headerEditEl.on('blur', function(e) {
4470                 this.toggleHeaderInput(false)
4471             },this);
4472         }
4473
4474     },
4475   
4476
4477     resize : function()
4478     {
4479         this.maskEl.setSize(
4480             Roo.lib.Dom.getViewWidth(true),
4481             Roo.lib.Dom.getViewHeight(true)
4482         );
4483         
4484         if (this.fitwindow) {
4485             
4486            this.dialogEl.setStyle( { 'max-width' : '100%' });
4487             this.setSize(
4488                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4489                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4490             );
4491             return;
4492         }
4493         
4494         if(this.max_width !== 0) {
4495             
4496             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4497             
4498             if(this.height) {
4499                 this.setSize(w, this.height);
4500                 return;
4501             }
4502             
4503             if(this.max_height) {
4504                 this.setSize(w,Math.min(
4505                     this.max_height,
4506                     Roo.lib.Dom.getViewportHeight(true) - 60
4507                 ));
4508                 
4509                 return;
4510             }
4511             
4512             if(!this.fit_content) {
4513                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4514                 return;
4515             }
4516             
4517             this.setSize(w, Math.min(
4518                 60 +
4519                 this.headerEl.getHeight() + 
4520                 this.footerEl.getHeight() + 
4521                 this.getChildHeight(this.bodyEl.dom.childNodes),
4522                 Roo.lib.Dom.getViewportHeight(true) - 60)
4523             );
4524         }
4525         
4526     },
4527
4528     setSize : function(w,h)
4529     {
4530         if (!w && !h) {
4531             return;
4532         }
4533         
4534         this.resizeTo(w,h);
4535     },
4536
4537     show : function() {
4538
4539         if (!this.rendered) {
4540             this.render();
4541         }
4542         this.toggleHeaderInput(false);
4543         //this.el.setStyle('display', 'block');
4544         this.el.removeClass('hideing');
4545         this.el.dom.style.display='block';
4546         
4547         Roo.get(document.body).addClass('modal-open');
4548  
4549         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4550             
4551             (function(){
4552                 this.el.addClass('show');
4553                 this.el.addClass('in');
4554             }).defer(50, this);
4555         }else{
4556             this.el.addClass('show');
4557             this.el.addClass('in');
4558         }
4559
4560         // not sure how we can show data in here..
4561         //if (this.tmpl) {
4562         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4563         //}
4564
4565         Roo.get(document.body).addClass("x-body-masked");
4566         
4567         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4568         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4569         this.maskEl.dom.style.display = 'block';
4570         this.maskEl.addClass('show');
4571         
4572         
4573         this.resize();
4574         
4575         this.fireEvent('show', this);
4576
4577         // set zindex here - otherwise it appears to be ignored...
4578         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4579
4580         (function () {
4581             this.items.forEach( function(e) {
4582                 e.layout ? e.layout() : false;
4583
4584             });
4585         }).defer(100,this);
4586
4587     },
4588     hide : function()
4589     {
4590         if(this.fireEvent("beforehide", this) !== false){
4591             
4592             this.maskEl.removeClass('show');
4593             
4594             this.maskEl.dom.style.display = '';
4595             Roo.get(document.body).removeClass("x-body-masked");
4596             this.el.removeClass('in');
4597             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4598
4599             if(this.animate){ // why
4600                 this.el.addClass('hideing');
4601                 this.el.removeClass('show');
4602                 (function(){
4603                     if (!this.el.hasClass('hideing')) {
4604                         return; // it's been shown again...
4605                     }
4606                     
4607                     this.el.dom.style.display='';
4608
4609                     Roo.get(document.body).removeClass('modal-open');
4610                     this.el.removeClass('hideing');
4611                 }).defer(150,this);
4612                 
4613             }else{
4614                 this.el.removeClass('show');
4615                 this.el.dom.style.display='';
4616                 Roo.get(document.body).removeClass('modal-open');
4617
4618             }
4619             this.fireEvent('hide', this);
4620         }
4621     },
4622     isVisible : function()
4623     {
4624         
4625         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4626         
4627     },
4628
4629     addButton : function(str, cb)
4630     {
4631
4632
4633         var b = Roo.apply({}, { html : str } );
4634         b.xns = b.xns || Roo.bootstrap;
4635         b.xtype = b.xtype || 'Button';
4636         if (typeof(b.listeners) == 'undefined') {
4637             b.listeners = { click : cb.createDelegate(this)  };
4638         }
4639
4640         var btn = Roo.factory(b);
4641
4642         btn.render(this.getButtonContainer());
4643
4644         return btn;
4645
4646     },
4647
4648     setDefaultButton : function(btn)
4649     {
4650         //this.el.select('.modal-footer').()
4651     },
4652
4653     resizeTo: function(w,h)
4654     {
4655         this.dialogEl.setWidth(w);
4656         
4657         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4658
4659         this.bodyEl.setHeight(h - diff);
4660         
4661         this.fireEvent('resize', this);
4662     },
4663     
4664     setContentSize  : function(w, h)
4665     {
4666
4667     },
4668     onButtonClick: function(btn,e)
4669     {
4670         //Roo.log([a,b,c]);
4671         this.fireEvent('btnclick', btn.name, e);
4672     },
4673      /**
4674      * Set the title of the Dialog
4675      * @param {String} str new Title
4676      */
4677     setTitle: function(str) {
4678         this.titleEl.dom.innerHTML = str;
4679         this.title = str;
4680     },
4681     /**
4682      * Set the body of the Dialog
4683      * @param {String} str new Title
4684      */
4685     setBody: function(str) {
4686         this.bodyEl.dom.innerHTML = str;
4687     },
4688     /**
4689      * Set the body of the Dialog using the template
4690      * @param {Obj} data - apply this data to the template and replace the body contents.
4691      */
4692     applyBody: function(obj)
4693     {
4694         if (!this.tmpl) {
4695             Roo.log("Error - using apply Body without a template");
4696             //code
4697         }
4698         this.tmpl.overwrite(this.bodyEl, obj);
4699     },
4700     
4701     getChildHeight : function(child_nodes)
4702     {
4703         if(
4704             !child_nodes ||
4705             child_nodes.length == 0
4706         ) {
4707             return 0;
4708         }
4709         
4710         var child_height = 0;
4711         
4712         for(var i = 0; i < child_nodes.length; i++) {
4713             
4714             /*
4715             * for modal with tabs...
4716             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4717                 
4718                 var layout_childs = child_nodes[i].childNodes;
4719                 
4720                 for(var j = 0; j < layout_childs.length; j++) {
4721                     
4722                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4723                         
4724                         var layout_body_childs = layout_childs[j].childNodes;
4725                         
4726                         for(var k = 0; k < layout_body_childs.length; k++) {
4727                             
4728                             if(layout_body_childs[k].classList.contains('navbar')) {
4729                                 child_height += layout_body_childs[k].offsetHeight;
4730                                 continue;
4731                             }
4732                             
4733                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4734                                 
4735                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4736                                 
4737                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4738                                     
4739                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4740                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4741                                         continue;
4742                                     }
4743                                     
4744                                 }
4745                                 
4746                             }
4747                             
4748                         }
4749                     }
4750                 }
4751                 continue;
4752             }
4753             */
4754             
4755             child_height += child_nodes[i].offsetHeight;
4756             // Roo.log(child_nodes[i].offsetHeight);
4757         }
4758         
4759         return child_height;
4760     },
4761     toggleHeaderInput : function(is_edit)
4762     {
4763         if (!this.editableTitle) {
4764             return; // not editable.
4765         }
4766         if (is_edit && this.is_header_editing) {
4767             return; // already editing..
4768         }
4769         if (is_edit) {
4770     
4771             this.headerEditEl.dom.value = this.title;
4772             this.headerEditEl.removeClass('d-none');
4773             this.headerEditEl.dom.focus();
4774             this.titleEl.addClass('d-none');
4775             
4776             this.is_header_editing = true;
4777             return
4778         }
4779         // flip back to not editing.
4780         this.title = this.headerEditEl.dom.value;
4781         this.headerEditEl.addClass('d-none');
4782         this.titleEl.removeClass('d-none');
4783         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4784         this.is_header_editing = false;
4785         this.fireEvent('titlechanged', this, this.title);
4786     
4787             
4788         
4789     }
4790
4791 });
4792
4793
4794 Roo.apply(Roo.bootstrap.Modal,  {
4795     /**
4796          * Button config that displays a single OK button
4797          * @type Object
4798          */
4799         OK :  [{
4800             name : 'ok',
4801             weight : 'primary',
4802             html : 'OK'
4803         }],
4804         /**
4805          * Button config that displays Yes and No buttons
4806          * @type Object
4807          */
4808         YESNO : [
4809             {
4810                 name  : 'no',
4811                 html : 'No'
4812             },
4813             {
4814                 name  :'yes',
4815                 weight : 'primary',
4816                 html : 'Yes'
4817             }
4818         ],
4819
4820         /**
4821          * Button config that displays OK and Cancel buttons
4822          * @type Object
4823          */
4824         OKCANCEL : [
4825             {
4826                name : 'cancel',
4827                 html : 'Cancel'
4828             },
4829             {
4830                 name : 'ok',
4831                 weight : 'primary',
4832                 html : 'OK'
4833             }
4834         ],
4835         /**
4836          * Button config that displays Yes, No and Cancel buttons
4837          * @type Object
4838          */
4839         YESNOCANCEL : [
4840             {
4841                 name : 'yes',
4842                 weight : 'primary',
4843                 html : 'Yes'
4844             },
4845             {
4846                 name : 'no',
4847                 html : 'No'
4848             },
4849             {
4850                 name : 'cancel',
4851                 html : 'Cancel'
4852             }
4853         ],
4854         
4855         zIndex : 10001
4856 });
4857
4858 /*
4859  * - LGPL
4860  *
4861  * messagebox - can be used as a replace
4862  * 
4863  */
4864 /**
4865  * @class Roo.MessageBox
4866  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4867  * Example usage:
4868  *<pre><code>
4869 // Basic alert:
4870 Roo.Msg.alert('Status', 'Changes saved successfully.');
4871
4872 // Prompt for user data:
4873 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4874     if (btn == 'ok'){
4875         // process text value...
4876     }
4877 });
4878
4879 // Show a dialog using config options:
4880 Roo.Msg.show({
4881    title:'Save Changes?',
4882    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4883    buttons: Roo.Msg.YESNOCANCEL,
4884    fn: processResult,
4885    animEl: 'elId'
4886 });
4887 </code></pre>
4888  * @singleton
4889  */
4890 Roo.bootstrap.MessageBox = function(){
4891     var dlg, opt, mask, waitTimer;
4892     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4893     var buttons, activeTextEl, bwidth;
4894
4895     
4896     // private
4897     var handleButton = function(button){
4898         dlg.hide();
4899         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4900     };
4901
4902     // private
4903     var handleHide = function(){
4904         if(opt && opt.cls){
4905             dlg.el.removeClass(opt.cls);
4906         }
4907         //if(waitTimer){
4908         //    Roo.TaskMgr.stop(waitTimer);
4909         //    waitTimer = null;
4910         //}
4911     };
4912
4913     // private
4914     var updateButtons = function(b){
4915         var width = 0;
4916         if(!b){
4917             buttons["ok"].hide();
4918             buttons["cancel"].hide();
4919             buttons["yes"].hide();
4920             buttons["no"].hide();
4921             dlg.footerEl.hide();
4922             
4923             return width;
4924         }
4925         dlg.footerEl.show();
4926         for(var k in buttons){
4927             if(typeof buttons[k] != "function"){
4928                 if(b[k]){
4929                     buttons[k].show();
4930                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4931                     width += buttons[k].el.getWidth()+15;
4932                 }else{
4933                     buttons[k].hide();
4934                 }
4935             }
4936         }
4937         return width;
4938     };
4939
4940     // private
4941     var handleEsc = function(d, k, e){
4942         if(opt && opt.closable !== false){
4943             dlg.hide();
4944         }
4945         if(e){
4946             e.stopEvent();
4947         }
4948     };
4949
4950     return {
4951         /**
4952          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4953          * @return {Roo.BasicDialog} The BasicDialog element
4954          */
4955         getDialog : function(){
4956            if(!dlg){
4957                 dlg = new Roo.bootstrap.Modal( {
4958                     //draggable: true,
4959                     //resizable:false,
4960                     //constraintoviewport:false,
4961                     //fixedcenter:true,
4962                     //collapsible : false,
4963                     //shim:true,
4964                     //modal: true,
4965                 //    width: 'auto',
4966                   //  height:100,
4967                     //buttonAlign:"center",
4968                     closeClick : function(){
4969                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4970                             handleButton("no");
4971                         }else{
4972                             handleButton("cancel");
4973                         }
4974                     }
4975                 });
4976                 dlg.render();
4977                 dlg.on("hide", handleHide);
4978                 mask = dlg.mask;
4979                 //dlg.addKeyListener(27, handleEsc);
4980                 buttons = {};
4981                 this.buttons = buttons;
4982                 var bt = this.buttonText;
4983                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4984                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4985                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4986                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4987                 //Roo.log(buttons);
4988                 bodyEl = dlg.bodyEl.createChild({
4989
4990                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4991                         '<textarea class="roo-mb-textarea"></textarea>' +
4992                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4993                 });
4994                 msgEl = bodyEl.dom.firstChild;
4995                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4996                 textboxEl.enableDisplayMode();
4997                 textboxEl.addKeyListener([10,13], function(){
4998                     if(dlg.isVisible() && opt && opt.buttons){
4999                         if(opt.buttons.ok){
5000                             handleButton("ok");
5001                         }else if(opt.buttons.yes){
5002                             handleButton("yes");
5003                         }
5004                     }
5005                 });
5006                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5007                 textareaEl.enableDisplayMode();
5008                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5009                 progressEl.enableDisplayMode();
5010                 
5011                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5012                 var pf = progressEl.dom.firstChild;
5013                 if (pf) {
5014                     pp = Roo.get(pf.firstChild);
5015                     pp.setHeight(pf.offsetHeight);
5016                 }
5017                 
5018             }
5019             return dlg;
5020         },
5021
5022         /**
5023          * Updates the message box body text
5024          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5025          * the XHTML-compliant non-breaking space character '&amp;#160;')
5026          * @return {Roo.MessageBox} This message box
5027          */
5028         updateText : function(text)
5029         {
5030             if(!dlg.isVisible() && !opt.width){
5031                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5032                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5033             }
5034             msgEl.innerHTML = text || '&#160;';
5035       
5036             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5037             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5038             var w = Math.max(
5039                     Math.min(opt.width || cw , this.maxWidth), 
5040                     Math.max(opt.minWidth || this.minWidth, bwidth)
5041             );
5042             if(opt.prompt){
5043                 activeTextEl.setWidth(w);
5044             }
5045             if(dlg.isVisible()){
5046                 dlg.fixedcenter = false;
5047             }
5048             // to big, make it scroll. = But as usual stupid IE does not support
5049             // !important..
5050             
5051             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5052                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5053                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5054             } else {
5055                 bodyEl.dom.style.height = '';
5056                 bodyEl.dom.style.overflowY = '';
5057             }
5058             if (cw > w) {
5059                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5060             } else {
5061                 bodyEl.dom.style.overflowX = '';
5062             }
5063             
5064             dlg.setContentSize(w, bodyEl.getHeight());
5065             if(dlg.isVisible()){
5066                 dlg.fixedcenter = true;
5067             }
5068             return this;
5069         },
5070
5071         /**
5072          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5073          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5074          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5075          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5076          * @return {Roo.MessageBox} This message box
5077          */
5078         updateProgress : function(value, text){
5079             if(text){
5080                 this.updateText(text);
5081             }
5082             
5083             if (pp) { // weird bug on my firefox - for some reason this is not defined
5084                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5085                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5086             }
5087             return this;
5088         },        
5089
5090         /**
5091          * Returns true if the message box is currently displayed
5092          * @return {Boolean} True if the message box is visible, else false
5093          */
5094         isVisible : function(){
5095             return dlg && dlg.isVisible();  
5096         },
5097
5098         /**
5099          * Hides the message box if it is displayed
5100          */
5101         hide : function(){
5102             if(this.isVisible()){
5103                 dlg.hide();
5104             }  
5105         },
5106
5107         /**
5108          * Displays a new message box, or reinitializes an existing message box, based on the config options
5109          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5110          * The following config object properties are supported:
5111          * <pre>
5112 Property    Type             Description
5113 ----------  ---------------  ------------------------------------------------------------------------------------
5114 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5115                                    closes (defaults to undefined)
5116 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5117                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5118 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5119                                    progress and wait dialogs will ignore this property and always hide the
5120                                    close button as they can only be closed programmatically.
5121 cls               String           A custom CSS class to apply to the message box element
5122 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5123                                    displayed (defaults to 75)
5124 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5125                                    function will be btn (the name of the button that was clicked, if applicable,
5126                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5127                                    Progress and wait dialogs will ignore this option since they do not respond to
5128                                    user actions and can only be closed programmatically, so any required function
5129                                    should be called by the same code after it closes the dialog.
5130 icon              String           A CSS class that provides a background image to be used as an icon for
5131                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5132 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5133 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5134 modal             Boolean          False to allow user interaction with the page while the message box is
5135                                    displayed (defaults to true)
5136 msg               String           A string that will replace the existing message box body text (defaults
5137                                    to the XHTML-compliant non-breaking space character '&#160;')
5138 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5139 progress          Boolean          True to display a progress bar (defaults to false)
5140 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5141 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5142 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5143 title             String           The title text
5144 value             String           The string value to set into the active textbox element if displayed
5145 wait              Boolean          True to display a progress bar (defaults to false)
5146 width             Number           The width of the dialog in pixels
5147 </pre>
5148          *
5149          * Example usage:
5150          * <pre><code>
5151 Roo.Msg.show({
5152    title: 'Address',
5153    msg: 'Please enter your address:',
5154    width: 300,
5155    buttons: Roo.MessageBox.OKCANCEL,
5156    multiline: true,
5157    fn: saveAddress,
5158    animEl: 'addAddressBtn'
5159 });
5160 </code></pre>
5161          * @param {Object} config Configuration options
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         show : function(options)
5165         {
5166             
5167             // this causes nightmares if you show one dialog after another
5168             // especially on callbacks..
5169              
5170             if(this.isVisible()){
5171                 
5172                 this.hide();
5173                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5174                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5175                 Roo.log("New Dialog Message:" +  options.msg )
5176                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5177                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5178                 
5179             }
5180             var d = this.getDialog();
5181             opt = options;
5182             d.setTitle(opt.title || "&#160;");
5183             d.closeEl.setDisplayed(opt.closable !== false);
5184             activeTextEl = textboxEl;
5185             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5186             if(opt.prompt){
5187                 if(opt.multiline){
5188                     textboxEl.hide();
5189                     textareaEl.show();
5190                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5191                         opt.multiline : this.defaultTextHeight);
5192                     activeTextEl = textareaEl;
5193                 }else{
5194                     textboxEl.show();
5195                     textareaEl.hide();
5196                 }
5197             }else{
5198                 textboxEl.hide();
5199                 textareaEl.hide();
5200             }
5201             progressEl.setDisplayed(opt.progress === true);
5202             if (opt.progress) {
5203                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5204             }
5205             this.updateProgress(0);
5206             activeTextEl.dom.value = opt.value || "";
5207             if(opt.prompt){
5208                 dlg.setDefaultButton(activeTextEl);
5209             }else{
5210                 var bs = opt.buttons;
5211                 var db = null;
5212                 if(bs && bs.ok){
5213                     db = buttons["ok"];
5214                 }else if(bs && bs.yes){
5215                     db = buttons["yes"];
5216                 }
5217                 dlg.setDefaultButton(db);
5218             }
5219             bwidth = updateButtons(opt.buttons);
5220             this.updateText(opt.msg);
5221             if(opt.cls){
5222                 d.el.addClass(opt.cls);
5223             }
5224             d.proxyDrag = opt.proxyDrag === true;
5225             d.modal = opt.modal !== false;
5226             d.mask = opt.modal !== false ? mask : false;
5227             if(!d.isVisible()){
5228                 // force it to the end of the z-index stack so it gets a cursor in FF
5229                 document.body.appendChild(dlg.el.dom);
5230                 d.animateTarget = null;
5231                 d.show(options.animEl);
5232             }
5233             return this;
5234         },
5235
5236         /**
5237          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5238          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5239          * and closing the message box when the process is complete.
5240          * @param {String} title The title bar text
5241          * @param {String} msg The message box body text
5242          * @return {Roo.MessageBox} This message box
5243          */
5244         progress : function(title, msg){
5245             this.show({
5246                 title : title,
5247                 msg : msg,
5248                 buttons: false,
5249                 progress:true,
5250                 closable:false,
5251                 minWidth: this.minProgressWidth,
5252                 modal : true
5253             });
5254             return this;
5255         },
5256
5257         /**
5258          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5259          * If a callback function is passed it will be called after the user clicks the button, and the
5260          * id of the button that was clicked will be passed as the only parameter to the callback
5261          * (could also be the top-right close button).
5262          * @param {String} title The title bar text
5263          * @param {String} msg The message box body text
5264          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5265          * @param {Object} scope (optional) The scope of the callback function
5266          * @return {Roo.MessageBox} This message box
5267          */
5268         alert : function(title, msg, fn, scope)
5269         {
5270             this.show({
5271                 title : title,
5272                 msg : msg,
5273                 buttons: this.OK,
5274                 fn: fn,
5275                 closable : false,
5276                 scope : scope,
5277                 modal : true
5278             });
5279             return this;
5280         },
5281
5282         /**
5283          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5284          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5285          * You are responsible for closing the message box when the process is complete.
5286          * @param {String} msg The message box body text
5287          * @param {String} title (optional) The title bar text
5288          * @return {Roo.MessageBox} This message box
5289          */
5290         wait : function(msg, title){
5291             this.show({
5292                 title : title,
5293                 msg : msg,
5294                 buttons: false,
5295                 closable:false,
5296                 progress:true,
5297                 modal:true,
5298                 width:300,
5299                 wait:true
5300             });
5301             waitTimer = Roo.TaskMgr.start({
5302                 run: function(i){
5303                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5304                 },
5305                 interval: 1000
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5312          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5313          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5314          * @param {String} title The title bar text
5315          * @param {String} msg The message box body text
5316          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5317          * @param {Object} scope (optional) The scope of the callback function
5318          * @return {Roo.MessageBox} This message box
5319          */
5320         confirm : function(title, msg, fn, scope){
5321             this.show({
5322                 title : title,
5323                 msg : msg,
5324                 buttons: this.YESNO,
5325                 fn: fn,
5326                 scope : scope,
5327                 modal : true
5328             });
5329             return this;
5330         },
5331
5332         /**
5333          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5334          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5335          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5336          * (could also be the top-right close button) and the text that was entered will be passed as the two
5337          * parameters to the callback.
5338          * @param {String} title The title bar text
5339          * @param {String} msg The message box body text
5340          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5341          * @param {Object} scope (optional) The scope of the callback function
5342          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5343          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5344          * @return {Roo.MessageBox} This message box
5345          */
5346         prompt : function(title, msg, fn, scope, multiline){
5347             this.show({
5348                 title : title,
5349                 msg : msg,
5350                 buttons: this.OKCANCEL,
5351                 fn: fn,
5352                 minWidth:250,
5353                 scope : scope,
5354                 prompt:true,
5355                 multiline: multiline,
5356                 modal : true
5357             });
5358             return this;
5359         },
5360
5361         /**
5362          * Button config that displays a single OK button
5363          * @type Object
5364          */
5365         OK : {ok:true},
5366         /**
5367          * Button config that displays Yes and No buttons
5368          * @type Object
5369          */
5370         YESNO : {yes:true, no:true},
5371         /**
5372          * Button config that displays OK and Cancel buttons
5373          * @type Object
5374          */
5375         OKCANCEL : {ok:true, cancel:true},
5376         /**
5377          * Button config that displays Yes, No and Cancel buttons
5378          * @type Object
5379          */
5380         YESNOCANCEL : {yes:true, no:true, cancel:true},
5381
5382         /**
5383          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5384          * @type Number
5385          */
5386         defaultTextHeight : 75,
5387         /**
5388          * The maximum width in pixels of the message box (defaults to 600)
5389          * @type Number
5390          */
5391         maxWidth : 600,
5392         /**
5393          * The minimum width in pixels of the message box (defaults to 100)
5394          * @type Number
5395          */
5396         minWidth : 100,
5397         /**
5398          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5399          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5400          * @type Number
5401          */
5402         minProgressWidth : 250,
5403         /**
5404          * An object containing the default button text strings that can be overriden for localized language support.
5405          * Supported properties are: ok, cancel, yes and no.
5406          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5407          * @type Object
5408          */
5409         buttonText : {
5410             ok : "OK",
5411             cancel : "Cancel",
5412             yes : "Yes",
5413             no : "No"
5414         }
5415     };
5416 }();
5417
5418 /**
5419  * Shorthand for {@link Roo.MessageBox}
5420  */
5421 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5422 Roo.Msg = Roo.Msg || Roo.MessageBox;
5423 /*
5424  * - LGPL
5425  *
5426  * navbar
5427  * 
5428  */
5429
5430 /**
5431  * @class Roo.bootstrap.Navbar
5432  * @extends Roo.bootstrap.Component
5433  * Bootstrap Navbar class
5434
5435  * @constructor
5436  * Create a new Navbar
5437  * @param {Object} config The config object
5438  */
5439
5440
5441 Roo.bootstrap.Navbar = function(config){
5442     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5443     this.addEvents({
5444         // raw events
5445         /**
5446          * @event beforetoggle
5447          * Fire before toggle the menu
5448          * @param {Roo.EventObject} e
5449          */
5450         "beforetoggle" : true
5451     });
5452 };
5453
5454 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5455     
5456     
5457    
5458     // private
5459     navItems : false,
5460     loadMask : false,
5461     
5462     
5463     getAutoCreate : function(){
5464         
5465         
5466         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5467         
5468     },
5469     
5470     initEvents :function ()
5471     {
5472         //Roo.log(this.el.select('.navbar-toggle',true));
5473         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5474         
5475         var mark = {
5476             tag: "div",
5477             cls:"x-dlg-mask"
5478         };
5479         
5480         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5481         
5482         var size = this.el.getSize();
5483         this.maskEl.setSize(size.width, size.height);
5484         this.maskEl.enableDisplayMode("block");
5485         this.maskEl.hide();
5486         
5487         if(this.loadMask){
5488             this.maskEl.show();
5489         }
5490     },
5491     
5492     
5493     getChildContainer : function()
5494     {
5495         if (this.el && this.el.select('.collapse').getCount()) {
5496             return this.el.select('.collapse',true).first();
5497         }
5498         
5499         return this.el;
5500     },
5501     
5502     mask : function()
5503     {
5504         this.maskEl.show();
5505     },
5506     
5507     unmask : function()
5508     {
5509         this.maskEl.hide();
5510     },
5511     onToggle : function()
5512     {
5513         
5514         if(this.fireEvent('beforetoggle', this) === false){
5515             return;
5516         }
5517         var ce = this.el.select('.navbar-collapse',true).first();
5518       
5519         if (!ce.hasClass('show')) {
5520            this.expand();
5521         } else {
5522             this.collapse();
5523         }
5524         
5525         
5526     
5527     },
5528     /**
5529      * Expand the navbar pulldown 
5530      */
5531     expand : function ()
5532     {
5533        
5534         var ce = this.el.select('.navbar-collapse',true).first();
5535         if (ce.hasClass('collapsing')) {
5536             return;
5537         }
5538         ce.dom.style.height = '';
5539                // show it...
5540         ce.addClass('in'); // old...
5541         ce.removeClass('collapse');
5542         ce.addClass('show');
5543         var h = ce.getHeight();
5544         Roo.log(h);
5545         ce.removeClass('show');
5546         // at this point we should be able to see it..
5547         ce.addClass('collapsing');
5548         
5549         ce.setHeight(0); // resize it ...
5550         ce.on('transitionend', function() {
5551             //Roo.log('done transition');
5552             ce.removeClass('collapsing');
5553             ce.addClass('show');
5554             ce.removeClass('collapse');
5555
5556             ce.dom.style.height = '';
5557         }, this, { single: true} );
5558         ce.setHeight(h);
5559         ce.dom.scrollTop = 0;
5560     },
5561     /**
5562      * Collapse the navbar pulldown 
5563      */
5564     collapse : function()
5565     {
5566          var ce = this.el.select('.navbar-collapse',true).first();
5567        
5568         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5569             // it's collapsed or collapsing..
5570             return;
5571         }
5572         ce.removeClass('in'); // old...
5573         ce.setHeight(ce.getHeight());
5574         ce.removeClass('show');
5575         ce.addClass('collapsing');
5576         
5577         ce.on('transitionend', function() {
5578             ce.dom.style.height = '';
5579             ce.removeClass('collapsing');
5580             ce.addClass('collapse');
5581         }, this, { single: true} );
5582         ce.setHeight(0);
5583     }
5584     
5585     
5586     
5587 });
5588
5589
5590
5591  
5592
5593  /*
5594  * - LGPL
5595  *
5596  * navbar
5597  * 
5598  */
5599
5600 /**
5601  * @class Roo.bootstrap.NavSimplebar
5602  * @extends Roo.bootstrap.Navbar
5603  * Bootstrap Sidebar class
5604  *
5605  * @cfg {Boolean} inverse is inverted color
5606  * 
5607  * @cfg {String} type (nav | pills | tabs)
5608  * @cfg {Boolean} arrangement stacked | justified
5609  * @cfg {String} align (left | right) alignment
5610  * 
5611  * @cfg {Boolean} main (true|false) main nav bar? default false
5612  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5613  * 
5614  * @cfg {String} tag (header|footer|nav|div) default is nav 
5615
5616  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5617  * 
5618  * 
5619  * @constructor
5620  * Create a new Sidebar
5621  * @param {Object} config The config object
5622  */
5623
5624
5625 Roo.bootstrap.NavSimplebar = function(config){
5626     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5627 };
5628
5629 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5630     
5631     inverse: false,
5632     
5633     type: false,
5634     arrangement: '',
5635     align : false,
5636     
5637     weight : 'light',
5638     
5639     main : false,
5640     
5641     
5642     tag : false,
5643     
5644     
5645     getAutoCreate : function(){
5646         
5647         
5648         var cfg = {
5649             tag : this.tag || 'div',
5650             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5651         };
5652         if (['light','white'].indexOf(this.weight) > -1) {
5653             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5654         }
5655         cfg.cls += ' bg-' + this.weight;
5656         
5657         if (this.inverse) {
5658             cfg.cls += ' navbar-inverse';
5659             
5660         }
5661         
5662         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5663         
5664         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5665             return cfg;
5666         }
5667         
5668         
5669     
5670         
5671         cfg.cn = [
5672             {
5673                 cls: 'nav nav-' + this.xtype,
5674                 tag : 'ul'
5675             }
5676         ];
5677         
5678          
5679         this.type = this.type || 'nav';
5680         if (['tabs','pills'].indexOf(this.type) != -1) {
5681             cfg.cn[0].cls += ' nav-' + this.type
5682         
5683         
5684         } else {
5685             if (this.type!=='nav') {
5686                 Roo.log('nav type must be nav/tabs/pills')
5687             }
5688             cfg.cn[0].cls += ' navbar-nav'
5689         }
5690         
5691         
5692         
5693         
5694         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5695             cfg.cn[0].cls += ' nav-' + this.arrangement;
5696         }
5697         
5698         
5699         if (this.align === 'right') {
5700             cfg.cn[0].cls += ' navbar-right';
5701         }
5702         
5703         
5704         
5705         
5706         return cfg;
5707     
5708         
5709     }
5710     
5711     
5712     
5713 });
5714
5715
5716
5717  
5718
5719  
5720        /*
5721  * - LGPL
5722  *
5723  * navbar
5724  * navbar-fixed-top
5725  * navbar-expand-md  fixed-top 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavHeaderbar
5730  * @extends Roo.bootstrap.NavSimplebar
5731  * Bootstrap Sidebar class
5732  *
5733  * @cfg {String} brand what is brand
5734  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5735  * @cfg {String} brand_href href of the brand
5736  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5737  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5738  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5739  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5740  * 
5741  * @constructor
5742  * Create a new Sidebar
5743  * @param {Object} config The config object
5744  */
5745
5746
5747 Roo.bootstrap.NavHeaderbar = function(config){
5748     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5749       
5750 };
5751
5752 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5753     
5754     position: '',
5755     brand: '',
5756     brand_href: false,
5757     srButton : true,
5758     autohide : false,
5759     desktopCenter : false,
5760    
5761     
5762     getAutoCreate : function(){
5763         
5764         var   cfg = {
5765             tag: this.nav || 'nav',
5766             cls: 'navbar navbar-expand-md',
5767             role: 'navigation',
5768             cn: []
5769         };
5770         
5771         var cn = cfg.cn;
5772         if (this.desktopCenter) {
5773             cn.push({cls : 'container', cn : []});
5774             cn = cn[0].cn;
5775         }
5776         
5777         if(this.srButton){
5778             var btn = {
5779                 tag: 'button',
5780                 type: 'button',
5781                 cls: 'navbar-toggle navbar-toggler',
5782                 'data-toggle': 'collapse',
5783                 cn: [
5784                     {
5785                         tag: 'span',
5786                         cls: 'sr-only',
5787                         html: 'Toggle navigation'
5788                     },
5789                     {
5790                         tag: 'span',
5791                         cls: 'icon-bar navbar-toggler-icon'
5792                     },
5793                     {
5794                         tag: 'span',
5795                         cls: 'icon-bar'
5796                     },
5797                     {
5798                         tag: 'span',
5799                         cls: 'icon-bar'
5800                     }
5801                 ]
5802             };
5803             
5804             cn.push( Roo.bootstrap.version == 4 ? btn : {
5805                 tag: 'div',
5806                 cls: 'navbar-header',
5807                 cn: [
5808                     btn
5809                 ]
5810             });
5811         }
5812         
5813         cn.push({
5814             tag: 'div',
5815             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5816             cn : []
5817         });
5818         
5819         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5820         
5821         if (['light','white'].indexOf(this.weight) > -1) {
5822             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5823         }
5824         cfg.cls += ' bg-' + this.weight;
5825         
5826         
5827         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5828             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5829             
5830             // tag can override this..
5831             
5832             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5833         }
5834         
5835         if (this.brand !== '') {
5836             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5837             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5838                 tag: 'a',
5839                 href: this.brand_href ? this.brand_href : '#',
5840                 cls: 'navbar-brand',
5841                 cn: [
5842                 this.brand
5843                 ]
5844             });
5845         }
5846         
5847         if(this.main){
5848             cfg.cls += ' main-nav';
5849         }
5850         
5851         
5852         return cfg;
5853
5854         
5855     },
5856     getHeaderChildContainer : function()
5857     {
5858         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5859             return this.el.select('.navbar-header',true).first();
5860         }
5861         
5862         return this.getChildContainer();
5863     },
5864     
5865     getChildContainer : function()
5866     {
5867          
5868         return this.el.select('.roo-navbar-collapse',true).first();
5869          
5870         
5871     },
5872     
5873     initEvents : function()
5874     {
5875         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5876         
5877         if (this.autohide) {
5878             
5879             var prevScroll = 0;
5880             var ft = this.el;
5881             
5882             Roo.get(document).on('scroll',function(e) {
5883                 var ns = Roo.get(document).getScroll().top;
5884                 var os = prevScroll;
5885                 prevScroll = ns;
5886                 
5887                 if(ns > os){
5888                     ft.removeClass('slideDown');
5889                     ft.addClass('slideUp');
5890                     return;
5891                 }
5892                 ft.removeClass('slideUp');
5893                 ft.addClass('slideDown');
5894                  
5895               
5896           },this);
5897         }
5898     }    
5899     
5900 });
5901
5902
5903
5904  
5905
5906  /*
5907  * - LGPL
5908  *
5909  * navbar
5910  * 
5911  */
5912
5913 /**
5914  * @class Roo.bootstrap.NavSidebar
5915  * @extends Roo.bootstrap.Navbar
5916  * Bootstrap Sidebar class
5917  * 
5918  * @constructor
5919  * Create a new Sidebar
5920  * @param {Object} config The config object
5921  */
5922
5923
5924 Roo.bootstrap.NavSidebar = function(config){
5925     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5926 };
5927
5928 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5929     
5930     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5931     
5932     getAutoCreate : function(){
5933         
5934         
5935         return  {
5936             tag: 'div',
5937             cls: 'sidebar sidebar-nav'
5938         };
5939     
5940         
5941     }
5942     
5943     
5944     
5945 });
5946
5947
5948
5949  
5950
5951  /*
5952  * - LGPL
5953  *
5954  * nav group
5955  * 
5956  */
5957
5958 /**
5959  * @class Roo.bootstrap.NavGroup
5960  * @extends Roo.bootstrap.Component
5961  * Bootstrap NavGroup class
5962  * @cfg {String} align (left|right)
5963  * @cfg {Boolean} inverse
5964  * @cfg {String} type (nav|pills|tab) default nav
5965  * @cfg {String} navId - reference Id for navbar.
5966  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5967  * 
5968  * @constructor
5969  * Create a new nav group
5970  * @param {Object} config The config object
5971  */
5972
5973 Roo.bootstrap.NavGroup = function(config){
5974     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5975     this.navItems = [];
5976    
5977     Roo.bootstrap.NavGroup.register(this);
5978      this.addEvents({
5979         /**
5980              * @event changed
5981              * Fires when the active item changes
5982              * @param {Roo.bootstrap.NavGroup} this
5983              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5984              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5985          */
5986         'changed': true
5987      });
5988     
5989 };
5990
5991 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5992     
5993     align: '',
5994     inverse: false,
5995     form: false,
5996     type: 'nav',
5997     navId : '',
5998     // private
5999     pilltype : true,
6000     
6001     navItems : false, 
6002     
6003     getAutoCreate : function()
6004     {
6005         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6006         
6007         cfg = {
6008             tag : 'ul',
6009             cls: 'nav' 
6010         };
6011         if (Roo.bootstrap.version == 4) {
6012             if (['tabs','pills'].indexOf(this.type) != -1) {
6013                 cfg.cls += ' nav-' + this.type; 
6014             } else {
6015                 // trying to remove so header bar can right align top?
6016                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6017                     // do not use on header bar... 
6018                     cfg.cls += ' navbar-nav';
6019                 }
6020             }
6021             
6022         } else {
6023             if (['tabs','pills'].indexOf(this.type) != -1) {
6024                 cfg.cls += ' nav-' + this.type
6025             } else {
6026                 if (this.type !== 'nav') {
6027                     Roo.log('nav type must be nav/tabs/pills')
6028                 }
6029                 cfg.cls += ' navbar-nav'
6030             }
6031         }
6032         
6033         if (this.parent() && this.parent().sidebar) {
6034             cfg = {
6035                 tag: 'ul',
6036                 cls: 'dashboard-menu sidebar-menu'
6037             };
6038             
6039             return cfg;
6040         }
6041         
6042         if (this.form === true) {
6043             cfg = {
6044                 tag: 'form',
6045                 cls: 'navbar-form form-inline'
6046             };
6047             //nav navbar-right ml-md-auto
6048             if (this.align === 'right') {
6049                 cfg.cls += ' navbar-right ml-md-auto';
6050             } else {
6051                 cfg.cls += ' navbar-left';
6052             }
6053         }
6054         
6055         if (this.align === 'right') {
6056             cfg.cls += ' navbar-right ml-md-auto';
6057         } else {
6058             cfg.cls += ' mr-auto';
6059         }
6060         
6061         if (this.inverse) {
6062             cfg.cls += ' navbar-inverse';
6063             
6064         }
6065         
6066         
6067         return cfg;
6068     },
6069     /**
6070     * sets the active Navigation item
6071     * @param {Roo.bootstrap.NavItem} the new current navitem
6072     */
6073     setActiveItem : function(item)
6074     {
6075         var prev = false;
6076         Roo.each(this.navItems, function(v){
6077             if (v == item) {
6078                 return ;
6079             }
6080             if (v.isActive()) {
6081                 v.setActive(false, true);
6082                 prev = v;
6083                 
6084             }
6085             
6086         });
6087
6088         item.setActive(true, true);
6089         this.fireEvent('changed', this, item, prev);
6090         
6091         
6092     },
6093     /**
6094     * gets the active Navigation item
6095     * @return {Roo.bootstrap.NavItem} the current navitem
6096     */
6097     getActive : function()
6098     {
6099         
6100         var prev = false;
6101         Roo.each(this.navItems, function(v){
6102             
6103             if (v.isActive()) {
6104                 prev = v;
6105                 
6106             }
6107             
6108         });
6109         return prev;
6110     },
6111     
6112     indexOfNav : function()
6113     {
6114         
6115         var prev = false;
6116         Roo.each(this.navItems, function(v,i){
6117             
6118             if (v.isActive()) {
6119                 prev = i;
6120                 
6121             }
6122             
6123         });
6124         return prev;
6125     },
6126     /**
6127     * adds a Navigation item
6128     * @param {Roo.bootstrap.NavItem} the navitem to add
6129     */
6130     addItem : function(cfg)
6131     {
6132         if (this.form && Roo.bootstrap.version == 4) {
6133             cfg.tag = 'div';
6134         }
6135         var cn = new Roo.bootstrap.NavItem(cfg);
6136         this.register(cn);
6137         cn.parentId = this.id;
6138         cn.onRender(this.el, null);
6139         return cn;
6140     },
6141     /**
6142     * register a Navigation item
6143     * @param {Roo.bootstrap.NavItem} the navitem to add
6144     */
6145     register : function(item)
6146     {
6147         this.navItems.push( item);
6148         item.navId = this.navId;
6149     
6150     },
6151     
6152     /**
6153     * clear all the Navigation item
6154     */
6155    
6156     clearAll : function()
6157     {
6158         this.navItems = [];
6159         this.el.dom.innerHTML = '';
6160     },
6161     
6162     getNavItem: function(tabId)
6163     {
6164         var ret = false;
6165         Roo.each(this.navItems, function(e) {
6166             if (e.tabId == tabId) {
6167                ret =  e;
6168                return false;
6169             }
6170             return true;
6171             
6172         });
6173         return ret;
6174     },
6175     
6176     setActiveNext : function()
6177     {
6178         var i = this.indexOfNav(this.getActive());
6179         if (i > this.navItems.length) {
6180             return;
6181         }
6182         this.setActiveItem(this.navItems[i+1]);
6183     },
6184     setActivePrev : function()
6185     {
6186         var i = this.indexOfNav(this.getActive());
6187         if (i  < 1) {
6188             return;
6189         }
6190         this.setActiveItem(this.navItems[i-1]);
6191     },
6192     clearWasActive : function(except) {
6193         Roo.each(this.navItems, function(e) {
6194             if (e.tabId != except.tabId && e.was_active) {
6195                e.was_active = false;
6196                return false;
6197             }
6198             return true;
6199             
6200         });
6201     },
6202     getWasActive : function ()
6203     {
6204         var r = false;
6205         Roo.each(this.navItems, function(e) {
6206             if (e.was_active) {
6207                r = e;
6208                return false;
6209             }
6210             return true;
6211             
6212         });
6213         return r;
6214     }
6215     
6216     
6217 });
6218
6219  
6220 Roo.apply(Roo.bootstrap.NavGroup, {
6221     
6222     groups: {},
6223      /**
6224     * register a Navigation Group
6225     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6226     */
6227     register : function(navgrp)
6228     {
6229         this.groups[navgrp.navId] = navgrp;
6230         
6231     },
6232     /**
6233     * fetch a Navigation Group based on the navigation ID
6234     * @param {string} the navgroup to add
6235     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6236     */
6237     get: function(navId) {
6238         if (typeof(this.groups[navId]) == 'undefined') {
6239             return false;
6240             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6241         }
6242         return this.groups[navId] ;
6243     }
6244     
6245     
6246     
6247 });
6248
6249  /*
6250  * - LGPL
6251  *
6252  * row
6253  * 
6254  */
6255
6256 /**
6257  * @class Roo.bootstrap.NavItem
6258  * @extends Roo.bootstrap.Component
6259  * Bootstrap Navbar.NavItem class
6260  * @cfg {String} href  link to
6261  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6262  * @cfg {Boolean} button_outline show and outlined button
6263  * @cfg {String} html content of button
6264  * @cfg {String} badge text inside badge
6265  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6266  * @cfg {String} glyphicon DEPRICATED - use fa
6267  * @cfg {String} icon DEPRICATED - use fa
6268  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6269  * @cfg {Boolean} active Is item active
6270  * @cfg {Boolean} disabled Is item disabled
6271  * @cfg {String} linkcls  Link Class
6272  * @cfg {Boolean} preventDefault (true | false) default false
6273  * @cfg {String} tabId the tab that this item activates.
6274  * @cfg {String} tagtype (a|span) render as a href or span?
6275  * @cfg {Boolean} animateRef (true|false) link to element default false  
6276   
6277  * @constructor
6278  * Create a new Navbar Item
6279  * @param {Object} config The config object
6280  */
6281 Roo.bootstrap.NavItem = function(config){
6282     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6283     this.addEvents({
6284         // raw events
6285         /**
6286          * @event click
6287          * The raw click event for the entire grid.
6288          * @param {Roo.EventObject} e
6289          */
6290         "click" : true,
6291          /**
6292             * @event changed
6293             * Fires when the active item active state changes
6294             * @param {Roo.bootstrap.NavItem} this
6295             * @param {boolean} state the new state
6296              
6297          */
6298         'changed': true,
6299         /**
6300             * @event scrollto
6301             * Fires when scroll to element
6302             * @param {Roo.bootstrap.NavItem} this
6303             * @param {Object} options
6304             * @param {Roo.EventObject} e
6305              
6306          */
6307         'scrollto': true
6308     });
6309    
6310 };
6311
6312 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6313     
6314     href: false,
6315     html: '',
6316     badge: '',
6317     icon: false,
6318     fa : false,
6319     glyphicon: false,
6320     active: false,
6321     preventDefault : false,
6322     tabId : false,
6323     tagtype : 'a',
6324     tag: 'li',
6325     disabled : false,
6326     animateRef : false,
6327     was_active : false,
6328     button_weight : '',
6329     button_outline : false,
6330     linkcls : '',
6331     navLink: false,
6332     
6333     getAutoCreate : function(){
6334          
6335         var cfg = {
6336             tag: this.tag,
6337             cls: 'nav-item'
6338         };
6339         
6340         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6341         
6342         if (this.active) {
6343             cfg.cls +=  ' active' ;
6344         }
6345         if (this.disabled) {
6346             cfg.cls += ' disabled';
6347         }
6348         
6349         // BS4 only?
6350         if (this.button_weight.length) {
6351             cfg.tag = this.href ? 'a' : 'button';
6352             cfg.html = this.html || '';
6353             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6354             if (this.href) {
6355                 cfg.href = this.href;
6356             }
6357             if (this.fa) {
6358                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6359             } else {
6360                 cfg.cls += " nav-html";
6361             }
6362             
6363             // menu .. should add dropdown-menu class - so no need for carat..
6364             
6365             if (this.badge !== '') {
6366                  
6367                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6368             }
6369             return cfg;
6370         }
6371         
6372         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6373             cfg.cn = [
6374                 {
6375                     tag: this.tagtype,
6376                     href : this.href || "#",
6377                     html: this.html || '',
6378                     cls : ''
6379                 }
6380             ];
6381             if (this.tagtype == 'a') {
6382                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6383         
6384             }
6385             if (this.icon) {
6386                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6387             } else  if (this.fa) {
6388                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6389             } else if(this.glyphicon) {
6390                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6391             } else {
6392                 cfg.cn[0].cls += " nav-html";
6393             }
6394             
6395             if (this.menu) {
6396                 cfg.cn[0].html += " <span class='caret'></span>";
6397              
6398             }
6399             
6400             if (this.badge !== '') {
6401                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6402             }
6403         }
6404         
6405         
6406         
6407         return cfg;
6408     },
6409     onRender : function(ct, position)
6410     {
6411        // Roo.log("Call onRender: " + this.xtype);
6412         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6413             this.tag = 'div';
6414         }
6415         
6416         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6417         this.navLink = this.el.select('.nav-link',true).first();
6418         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6419         return ret;
6420     },
6421       
6422     
6423     initEvents: function() 
6424     {
6425         if (typeof (this.menu) != 'undefined') {
6426             this.menu.parentType = this.xtype;
6427             this.menu.triggerEl = this.el;
6428             this.menu = this.addxtype(Roo.apply({}, this.menu));
6429         }
6430         
6431         this.el.on('click', this.onClick, this);
6432         
6433         //if(this.tagtype == 'span'){
6434         //    this.el.select('span',true).on('click', this.onClick, this);
6435         //}
6436        
6437         // at this point parent should be available..
6438         this.parent().register(this);
6439     },
6440     
6441     onClick : function(e)
6442     {
6443         if (e.getTarget('.dropdown-menu-item')) {
6444             // did you click on a menu itemm.... - then don't trigger onclick..
6445             return;
6446         }
6447         
6448         if(
6449                 this.preventDefault || 
6450                 this.href == '#' 
6451         ){
6452             Roo.log("NavItem - prevent Default?");
6453             e.preventDefault();
6454         }
6455         
6456         if (this.disabled) {
6457             return;
6458         }
6459         
6460         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6461         if (tg && tg.transition) {
6462             Roo.log("waiting for the transitionend");
6463             return;
6464         }
6465         
6466         
6467         
6468         //Roo.log("fire event clicked");
6469         if(this.fireEvent('click', this, e) === false){
6470             return;
6471         };
6472         
6473         if(this.tagtype == 'span'){
6474             return;
6475         }
6476         
6477         //Roo.log(this.href);
6478         var ael = this.el.select('a',true).first();
6479         //Roo.log(ael);
6480         
6481         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6482             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6483             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6484                 return; // ignore... - it's a 'hash' to another page.
6485             }
6486             Roo.log("NavItem - prevent Default?");
6487             e.preventDefault();
6488             this.scrollToElement(e);
6489         }
6490         
6491         
6492         var p =  this.parent();
6493    
6494         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6495             if (typeof(p.setActiveItem) !== 'undefined') {
6496                 p.setActiveItem(this);
6497             }
6498         }
6499         
6500         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6501         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6502             // remove the collapsed menu expand...
6503             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6504         }
6505     },
6506     
6507     isActive: function () {
6508         return this.active
6509     },
6510     setActive : function(state, fire, is_was_active)
6511     {
6512         if (this.active && !state && this.navId) {
6513             this.was_active = true;
6514             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6515             if (nv) {
6516                 nv.clearWasActive(this);
6517             }
6518             
6519         }
6520         this.active = state;
6521         
6522         if (!state ) {
6523             this.el.removeClass('active');
6524             this.navLink ? this.navLink.removeClass('active') : false;
6525         } else if (!this.el.hasClass('active')) {
6526             
6527             this.el.addClass('active');
6528             if (Roo.bootstrap.version == 4 && this.navLink ) {
6529                 this.navLink.addClass('active');
6530             }
6531             
6532         }
6533         if (fire) {
6534             this.fireEvent('changed', this, state);
6535         }
6536         
6537         // show a panel if it's registered and related..
6538         
6539         if (!this.navId || !this.tabId || !state || is_was_active) {
6540             return;
6541         }
6542         
6543         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6544         if (!tg) {
6545             return;
6546         }
6547         var pan = tg.getPanelByName(this.tabId);
6548         if (!pan) {
6549             return;
6550         }
6551         // if we can not flip to new panel - go back to old nav highlight..
6552         if (false == tg.showPanel(pan)) {
6553             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6554             if (nv) {
6555                 var onav = nv.getWasActive();
6556                 if (onav) {
6557                     onav.setActive(true, false, true);
6558                 }
6559             }
6560             
6561         }
6562         
6563         
6564         
6565     },
6566      // this should not be here...
6567     setDisabled : function(state)
6568     {
6569         this.disabled = state;
6570         if (!state ) {
6571             this.el.removeClass('disabled');
6572         } else if (!this.el.hasClass('disabled')) {
6573             this.el.addClass('disabled');
6574         }
6575         
6576     },
6577     
6578     /**
6579      * Fetch the element to display the tooltip on.
6580      * @return {Roo.Element} defaults to this.el
6581      */
6582     tooltipEl : function()
6583     {
6584         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6585     },
6586     
6587     scrollToElement : function(e)
6588     {
6589         var c = document.body;
6590         
6591         /*
6592          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6593          */
6594         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6595             c = document.documentElement;
6596         }
6597         
6598         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6599         
6600         if(!target){
6601             return;
6602         }
6603
6604         var o = target.calcOffsetsTo(c);
6605         
6606         var options = {
6607             target : target,
6608             value : o[1]
6609         };
6610         
6611         this.fireEvent('scrollto', this, options, e);
6612         
6613         Roo.get(c).scrollTo('top', options.value, true);
6614         
6615         return;
6616     },
6617     /**
6618      * Set the HTML (text content) of the item
6619      * @param {string} html  content for the nav item
6620      */
6621     setHtml : function(html)
6622     {
6623         this.html = html;
6624         this.htmlEl.dom.innerHTML = html;
6625         
6626     } 
6627 });
6628  
6629
6630  /*
6631  * - LGPL
6632  *
6633  * sidebar item
6634  *
6635  *  li
6636  *    <span> icon </span>
6637  *    <span> text </span>
6638  *    <span>badge </span>
6639  */
6640
6641 /**
6642  * @class Roo.bootstrap.NavSidebarItem
6643  * @extends Roo.bootstrap.NavItem
6644  * Bootstrap Navbar.NavSidebarItem class
6645  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6646  * {Boolean} open is the menu open
6647  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6648  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6649  * {String} buttonSize (sm|md|lg)the extra classes for the button
6650  * {Boolean} showArrow show arrow next to the text (default true)
6651  * @constructor
6652  * Create a new Navbar Button
6653  * @param {Object} config The config object
6654  */
6655 Roo.bootstrap.NavSidebarItem = function(config){
6656     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6657     this.addEvents({
6658         // raw events
6659         /**
6660          * @event click
6661          * The raw click event for the entire grid.
6662          * @param {Roo.EventObject} e
6663          */
6664         "click" : true,
6665          /**
6666             * @event changed
6667             * Fires when the active item active state changes
6668             * @param {Roo.bootstrap.NavSidebarItem} this
6669             * @param {boolean} state the new state
6670              
6671          */
6672         'changed': true
6673     });
6674    
6675 };
6676
6677 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6678     
6679     badgeWeight : 'default',
6680     
6681     open: false,
6682     
6683     buttonView : false,
6684     
6685     buttonWeight : 'default',
6686     
6687     buttonSize : 'md',
6688     
6689     showArrow : true,
6690     
6691     getAutoCreate : function(){
6692         
6693         
6694         var a = {
6695                 tag: 'a',
6696                 href : this.href || '#',
6697                 cls: '',
6698                 html : '',
6699                 cn : []
6700         };
6701         
6702         if(this.buttonView){
6703             a = {
6704                 tag: 'button',
6705                 href : this.href || '#',
6706                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6707                 html : this.html,
6708                 cn : []
6709             };
6710         }
6711         
6712         var cfg = {
6713             tag: 'li',
6714             cls: '',
6715             cn: [ a ]
6716         };
6717         
6718         if (this.active) {
6719             cfg.cls += ' active';
6720         }
6721         
6722         if (this.disabled) {
6723             cfg.cls += ' disabled';
6724         }
6725         if (this.open) {
6726             cfg.cls += ' open x-open';
6727         }
6728         // left icon..
6729         if (this.glyphicon || this.icon) {
6730             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6731             a.cn.push({ tag : 'i', cls : c }) ;
6732         }
6733         
6734         if(!this.buttonView){
6735             var span = {
6736                 tag: 'span',
6737                 html : this.html || ''
6738             };
6739
6740             a.cn.push(span);
6741             
6742         }
6743         
6744         if (this.badge !== '') {
6745             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6746         }
6747         
6748         if (this.menu) {
6749             
6750             if(this.showArrow){
6751                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6752             }
6753             
6754             a.cls += ' dropdown-toggle treeview' ;
6755         }
6756         
6757         return cfg;
6758     },
6759     
6760     initEvents : function()
6761     { 
6762         if (typeof (this.menu) != 'undefined') {
6763             this.menu.parentType = this.xtype;
6764             this.menu.triggerEl = this.el;
6765             this.menu = this.addxtype(Roo.apply({}, this.menu));
6766         }
6767         
6768         this.el.on('click', this.onClick, this);
6769         
6770         if(this.badge !== ''){
6771             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6772         }
6773         
6774     },
6775     
6776     onClick : function(e)
6777     {
6778         if(this.disabled){
6779             e.preventDefault();
6780             return;
6781         }
6782         
6783         if(this.preventDefault){
6784             e.preventDefault();
6785         }
6786         
6787         this.fireEvent('click', this, e);
6788     },
6789     
6790     disable : function()
6791     {
6792         this.setDisabled(true);
6793     },
6794     
6795     enable : function()
6796     {
6797         this.setDisabled(false);
6798     },
6799     
6800     setDisabled : function(state)
6801     {
6802         if(this.disabled == state){
6803             return;
6804         }
6805         
6806         this.disabled = state;
6807         
6808         if (state) {
6809             this.el.addClass('disabled');
6810             return;
6811         }
6812         
6813         this.el.removeClass('disabled');
6814         
6815         return;
6816     },
6817     
6818     setActive : function(state)
6819     {
6820         if(this.active == state){
6821             return;
6822         }
6823         
6824         this.active = state;
6825         
6826         if (state) {
6827             this.el.addClass('active');
6828             return;
6829         }
6830         
6831         this.el.removeClass('active');
6832         
6833         return;
6834     },
6835     
6836     isActive: function () 
6837     {
6838         return this.active;
6839     },
6840     
6841     setBadge : function(str)
6842     {
6843         if(!this.badgeEl){
6844             return;
6845         }
6846         
6847         this.badgeEl.dom.innerHTML = str;
6848     }
6849     
6850    
6851      
6852  
6853 });
6854  
6855
6856  /*
6857  * - LGPL
6858  *
6859  *  Breadcrumb Nav
6860  * 
6861  */
6862 Roo.namespace('Roo.bootstrap.breadcrumb');
6863
6864
6865 /**
6866  * @class Roo.bootstrap.breadcrumb.Nav
6867  * @extends Roo.bootstrap.Component
6868  * Bootstrap Breadcrumb Nav Class
6869  *  
6870  * @children Roo.bootstrap.breadcrumb.Item
6871  * 
6872  * @constructor
6873  * Create a new breadcrumb.Nav
6874  * @param {Object} config The config object
6875  */
6876
6877
6878 Roo.bootstrap.breadcrumb.Nav = function(config){
6879     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6880     
6881     
6882 };
6883
6884 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6885     
6886     getAutoCreate : function()
6887     {
6888
6889         var cfg = {
6890             tag: 'nav',
6891             cn : [
6892                 {
6893                     tag : 'ol',
6894                     cls : 'breadcrumb'
6895                 }
6896             ]
6897             
6898         };
6899           
6900         return cfg;
6901     },
6902     
6903     initEvents: function()
6904     {
6905         this.olEl = this.el.select('ol',true).first();    
6906     },
6907     getChildContainer : function()
6908     {
6909         return this.olEl;  
6910     }
6911     
6912 });
6913
6914  /*
6915  * - LGPL
6916  *
6917  *  Breadcrumb Item
6918  * 
6919  */
6920
6921
6922 /**
6923  * @class Roo.bootstrap.breadcrumb.Nav
6924  * @extends Roo.bootstrap.Component
6925  * Bootstrap Breadcrumb Nav Class
6926  *  
6927  * @children Roo.bootstrap.breadcrumb.Component
6928  * @cfg {String} html the content of the link.
6929  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6930  * @cfg {Boolean} active is it active
6931
6932  * 
6933  * @constructor
6934  * Create a new breadcrumb.Nav
6935  * @param {Object} config The config object
6936  */
6937
6938 Roo.bootstrap.breadcrumb.Item = function(config){
6939     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6940     this.addEvents({
6941         // img events
6942         /**
6943          * @event click
6944          * The img click event for the img.
6945          * @param {Roo.EventObject} e
6946          */
6947         "click" : true
6948     });
6949     
6950 };
6951
6952 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6953     
6954     href: false,
6955     html : '',
6956     
6957     getAutoCreate : function()
6958     {
6959
6960         var cfg = {
6961             tag: 'li',
6962             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6963         };
6964         if (this.href !== false) {
6965             cfg.cn = [{
6966                 tag : 'a',
6967                 href : this.href,
6968                 html : this.html
6969             }];
6970         } else {
6971             cfg.html = this.html;
6972         }
6973         
6974         return cfg;
6975     },
6976     
6977     initEvents: function()
6978     {
6979         if (this.href) {
6980             this.el.select('a', true).first().on('click',this.onClick, this)
6981         }
6982         
6983     },
6984     onClick : function(e)
6985     {
6986         e.preventDefault();
6987         this.fireEvent('click',this,  e);
6988     }
6989     
6990 });
6991
6992  /*
6993  * - LGPL
6994  *
6995  * row
6996  * 
6997  */
6998
6999 /**
7000  * @class Roo.bootstrap.Row
7001  * @extends Roo.bootstrap.Component
7002  * Bootstrap Row class (contains columns...)
7003  * 
7004  * @constructor
7005  * Create a new Row
7006  * @param {Object} config The config object
7007  */
7008
7009 Roo.bootstrap.Row = function(config){
7010     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7011 };
7012
7013 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7014     
7015     getAutoCreate : function(){
7016        return {
7017             cls: 'row clearfix'
7018        };
7019     }
7020     
7021     
7022 });
7023
7024  
7025
7026  /*
7027  * - LGPL
7028  *
7029  * pagination
7030  * 
7031  */
7032
7033 /**
7034  * @class Roo.bootstrap.Pagination
7035  * @extends Roo.bootstrap.Component
7036  * Bootstrap Pagination class
7037  * @cfg {String} size xs | sm | md | lg
7038  * @cfg {Boolean} inverse false | true
7039  * 
7040  * @constructor
7041  * Create a new Pagination
7042  * @param {Object} config The config object
7043  */
7044
7045 Roo.bootstrap.Pagination = function(config){
7046     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7047 };
7048
7049 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7050     
7051     cls: false,
7052     size: false,
7053     inverse: false,
7054     
7055     getAutoCreate : function(){
7056         var cfg = {
7057             tag: 'ul',
7058                 cls: 'pagination'
7059         };
7060         if (this.inverse) {
7061             cfg.cls += ' inverse';
7062         }
7063         if (this.html) {
7064             cfg.html=this.html;
7065         }
7066         if (this.cls) {
7067             cfg.cls += " " + this.cls;
7068         }
7069         return cfg;
7070     }
7071    
7072 });
7073
7074  
7075
7076  /*
7077  * - LGPL
7078  *
7079  * Pagination item
7080  * 
7081  */
7082
7083
7084 /**
7085  * @class Roo.bootstrap.PaginationItem
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap PaginationItem class
7088  * @cfg {String} html text
7089  * @cfg {String} href the link
7090  * @cfg {Boolean} preventDefault (true | false) default true
7091  * @cfg {Boolean} active (true | false) default false
7092  * @cfg {Boolean} disabled default false
7093  * 
7094  * 
7095  * @constructor
7096  * Create a new PaginationItem
7097  * @param {Object} config The config object
7098  */
7099
7100
7101 Roo.bootstrap.PaginationItem = function(config){
7102     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7103     this.addEvents({
7104         // raw events
7105         /**
7106          * @event click
7107          * The raw click event for the entire grid.
7108          * @param {Roo.EventObject} e
7109          */
7110         "click" : true
7111     });
7112 };
7113
7114 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7115     
7116     href : false,
7117     html : false,
7118     preventDefault: true,
7119     active : false,
7120     cls : false,
7121     disabled: false,
7122     
7123     getAutoCreate : function(){
7124         var cfg= {
7125             tag: 'li',
7126             cn: [
7127                 {
7128                     tag : 'a',
7129                     href : this.href ? this.href : '#',
7130                     html : this.html ? this.html : ''
7131                 }
7132             ]
7133         };
7134         
7135         if(this.cls){
7136             cfg.cls = this.cls;
7137         }
7138         
7139         if(this.disabled){
7140             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7141         }
7142         
7143         if(this.active){
7144             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7145         }
7146         
7147         return cfg;
7148     },
7149     
7150     initEvents: function() {
7151         
7152         this.el.on('click', this.onClick, this);
7153         
7154     },
7155     onClick : function(e)
7156     {
7157         Roo.log('PaginationItem on click ');
7158         if(this.preventDefault){
7159             e.preventDefault();
7160         }
7161         
7162         if(this.disabled){
7163             return;
7164         }
7165         
7166         this.fireEvent('click', this, e);
7167     }
7168    
7169 });
7170
7171  
7172
7173  /*
7174  * - LGPL
7175  *
7176  * slider
7177  * 
7178  */
7179
7180
7181 /**
7182  * @class Roo.bootstrap.Slider
7183  * @extends Roo.bootstrap.Component
7184  * Bootstrap Slider class
7185  *    
7186  * @constructor
7187  * Create a new Slider
7188  * @param {Object} config The config object
7189  */
7190
7191 Roo.bootstrap.Slider = function(config){
7192     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7193 };
7194
7195 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7196     
7197     getAutoCreate : function(){
7198         
7199         var cfg = {
7200             tag: 'div',
7201             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7202             cn: [
7203                 {
7204                     tag: 'a',
7205                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7206                 }
7207             ]
7208         };
7209         
7210         return cfg;
7211     }
7212    
7213 });
7214
7215  /*
7216  * Based on:
7217  * Ext JS Library 1.1.1
7218  * Copyright(c) 2006-2007, Ext JS, LLC.
7219  *
7220  * Originally Released Under LGPL - original licence link has changed is not relivant.
7221  *
7222  * Fork - LGPL
7223  * <script type="text/javascript">
7224  */
7225  
7226
7227 /**
7228  * @class Roo.grid.ColumnModel
7229  * @extends Roo.util.Observable
7230  * This is the default implementation of a ColumnModel used by the Grid. It defines
7231  * the columns in the grid.
7232  * <br>Usage:<br>
7233  <pre><code>
7234  var colModel = new Roo.grid.ColumnModel([
7235         {header: "Ticker", width: 60, sortable: true, locked: true},
7236         {header: "Company Name", width: 150, sortable: true},
7237         {header: "Market Cap.", width: 100, sortable: true},
7238         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7239         {header: "Employees", width: 100, sortable: true, resizable: false}
7240  ]);
7241  </code></pre>
7242  * <p>
7243  
7244  * The config options listed for this class are options which may appear in each
7245  * individual column definition.
7246  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7247  * @constructor
7248  * @param {Object} config An Array of column config objects. See this class's
7249  * config objects for details.
7250 */
7251 Roo.grid.ColumnModel = function(config){
7252         /**
7253      * The config passed into the constructor
7254      */
7255     this.config = config;
7256     this.lookup = {};
7257
7258     // if no id, create one
7259     // if the column does not have a dataIndex mapping,
7260     // map it to the order it is in the config
7261     for(var i = 0, len = config.length; i < len; i++){
7262         var c = config[i];
7263         if(typeof c.dataIndex == "undefined"){
7264             c.dataIndex = i;
7265         }
7266         if(typeof c.renderer == "string"){
7267             c.renderer = Roo.util.Format[c.renderer];
7268         }
7269         if(typeof c.id == "undefined"){
7270             c.id = Roo.id();
7271         }
7272         if(c.editor && c.editor.xtype){
7273             c.editor  = Roo.factory(c.editor, Roo.grid);
7274         }
7275         if(c.editor && c.editor.isFormField){
7276             c.editor = new Roo.grid.GridEditor(c.editor);
7277         }
7278         this.lookup[c.id] = c;
7279     }
7280
7281     /**
7282      * The width of columns which have no width specified (defaults to 100)
7283      * @type Number
7284      */
7285     this.defaultWidth = 100;
7286
7287     /**
7288      * Default sortable of columns which have no sortable specified (defaults to false)
7289      * @type Boolean
7290      */
7291     this.defaultSortable = false;
7292
7293     this.addEvents({
7294         /**
7295              * @event widthchange
7296              * Fires when the width of a column changes.
7297              * @param {ColumnModel} this
7298              * @param {Number} columnIndex The column index
7299              * @param {Number} newWidth The new width
7300              */
7301             "widthchange": true,
7302         /**
7303              * @event headerchange
7304              * Fires when the text of a header changes.
7305              * @param {ColumnModel} this
7306              * @param {Number} columnIndex The column index
7307              * @param {Number} newText The new header text
7308              */
7309             "headerchange": true,
7310         /**
7311              * @event hiddenchange
7312              * Fires when a column is hidden or "unhidden".
7313              * @param {ColumnModel} this
7314              * @param {Number} columnIndex The column index
7315              * @param {Boolean} hidden true if hidden, false otherwise
7316              */
7317             "hiddenchange": true,
7318             /**
7319          * @event columnmoved
7320          * Fires when a column is moved.
7321          * @param {ColumnModel} this
7322          * @param {Number} oldIndex
7323          * @param {Number} newIndex
7324          */
7325         "columnmoved" : true,
7326         /**
7327          * @event columlockchange
7328          * Fires when a column's locked state is changed
7329          * @param {ColumnModel} this
7330          * @param {Number} colIndex
7331          * @param {Boolean} locked true if locked
7332          */
7333         "columnlockchange" : true
7334     });
7335     Roo.grid.ColumnModel.superclass.constructor.call(this);
7336 };
7337 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7338     /**
7339      * @cfg {String} header The header text to display in the Grid view.
7340      */
7341     /**
7342      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7343      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7344      * specified, the column's index is used as an index into the Record's data Array.
7345      */
7346     /**
7347      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7348      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7349      */
7350     /**
7351      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7352      * Defaults to the value of the {@link #defaultSortable} property.
7353      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7354      */
7355     /**
7356      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7357      */
7358     /**
7359      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7360      */
7361     /**
7362      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7363      */
7364     /**
7365      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7366      */
7367     /**
7368      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7369      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7370      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7371      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7372      */
7373        /**
7374      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7375      */
7376     /**
7377      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7378      */
7379     /**
7380      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7381      */
7382     /**
7383      * @cfg {String} cursor (Optional)
7384      */
7385     /**
7386      * @cfg {String} tooltip (Optional)
7387      */
7388     /**
7389      * @cfg {Number} xs (Optional)
7390      */
7391     /**
7392      * @cfg {Number} sm (Optional)
7393      */
7394     /**
7395      * @cfg {Number} md (Optional)
7396      */
7397     /**
7398      * @cfg {Number} lg (Optional)
7399      */
7400     /**
7401      * Returns the id of the column at the specified index.
7402      * @param {Number} index The column index
7403      * @return {String} the id
7404      */
7405     getColumnId : function(index){
7406         return this.config[index].id;
7407     },
7408
7409     /**
7410      * Returns the column for a specified id.
7411      * @param {String} id The column id
7412      * @return {Object} the column
7413      */
7414     getColumnById : function(id){
7415         return this.lookup[id];
7416     },
7417
7418     
7419     /**
7420      * Returns the column for a specified dataIndex.
7421      * @param {String} dataIndex The column dataIndex
7422      * @return {Object|Boolean} the column or false if not found
7423      */
7424     getColumnByDataIndex: function(dataIndex){
7425         var index = this.findColumnIndex(dataIndex);
7426         return index > -1 ? this.config[index] : false;
7427     },
7428     
7429     /**
7430      * Returns the index for a specified column id.
7431      * @param {String} id The column id
7432      * @return {Number} the index, or -1 if not found
7433      */
7434     getIndexById : function(id){
7435         for(var i = 0, len = this.config.length; i < len; i++){
7436             if(this.config[i].id == id){
7437                 return i;
7438             }
7439         }
7440         return -1;
7441     },
7442     
7443     /**
7444      * Returns the index for a specified column dataIndex.
7445      * @param {String} dataIndex The column dataIndex
7446      * @return {Number} the index, or -1 if not found
7447      */
7448     
7449     findColumnIndex : function(dataIndex){
7450         for(var i = 0, len = this.config.length; i < len; i++){
7451             if(this.config[i].dataIndex == dataIndex){
7452                 return i;
7453             }
7454         }
7455         return -1;
7456     },
7457     
7458     
7459     moveColumn : function(oldIndex, newIndex){
7460         var c = this.config[oldIndex];
7461         this.config.splice(oldIndex, 1);
7462         this.config.splice(newIndex, 0, c);
7463         this.dataMap = null;
7464         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7465     },
7466
7467     isLocked : function(colIndex){
7468         return this.config[colIndex].locked === true;
7469     },
7470
7471     setLocked : function(colIndex, value, suppressEvent){
7472         if(this.isLocked(colIndex) == value){
7473             return;
7474         }
7475         this.config[colIndex].locked = value;
7476         if(!suppressEvent){
7477             this.fireEvent("columnlockchange", this, colIndex, value);
7478         }
7479     },
7480
7481     getTotalLockedWidth : function(){
7482         var totalWidth = 0;
7483         for(var i = 0; i < this.config.length; i++){
7484             if(this.isLocked(i) && !this.isHidden(i)){
7485                 this.totalWidth += this.getColumnWidth(i);
7486             }
7487         }
7488         return totalWidth;
7489     },
7490
7491     getLockedCount : function(){
7492         for(var i = 0, len = this.config.length; i < len; i++){
7493             if(!this.isLocked(i)){
7494                 return i;
7495             }
7496         }
7497         
7498         return this.config.length;
7499     },
7500
7501     /**
7502      * Returns the number of columns.
7503      * @return {Number}
7504      */
7505     getColumnCount : function(visibleOnly){
7506         if(visibleOnly === true){
7507             var c = 0;
7508             for(var i = 0, len = this.config.length; i < len; i++){
7509                 if(!this.isHidden(i)){
7510                     c++;
7511                 }
7512             }
7513             return c;
7514         }
7515         return this.config.length;
7516     },
7517
7518     /**
7519      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7520      * @param {Function} fn
7521      * @param {Object} scope (optional)
7522      * @return {Array} result
7523      */
7524     getColumnsBy : function(fn, scope){
7525         var r = [];
7526         for(var i = 0, len = this.config.length; i < len; i++){
7527             var c = this.config[i];
7528             if(fn.call(scope||this, c, i) === true){
7529                 r[r.length] = c;
7530             }
7531         }
7532         return r;
7533     },
7534
7535     /**
7536      * Returns true if the specified column is sortable.
7537      * @param {Number} col The column index
7538      * @return {Boolean}
7539      */
7540     isSortable : function(col){
7541         if(typeof this.config[col].sortable == "undefined"){
7542             return this.defaultSortable;
7543         }
7544         return this.config[col].sortable;
7545     },
7546
7547     /**
7548      * Returns the rendering (formatting) function defined for the column.
7549      * @param {Number} col The column index.
7550      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7551      */
7552     getRenderer : function(col){
7553         if(!this.config[col].renderer){
7554             return Roo.grid.ColumnModel.defaultRenderer;
7555         }
7556         return this.config[col].renderer;
7557     },
7558
7559     /**
7560      * Sets the rendering (formatting) function for a column.
7561      * @param {Number} col The column index
7562      * @param {Function} fn The function to use to process the cell's raw data
7563      * to return HTML markup for the grid view. The render function is called with
7564      * the following parameters:<ul>
7565      * <li>Data value.</li>
7566      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7567      * <li>css A CSS style string to apply to the table cell.</li>
7568      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7569      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7570      * <li>Row index</li>
7571      * <li>Column index</li>
7572      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7573      */
7574     setRenderer : function(col, fn){
7575         this.config[col].renderer = fn;
7576     },
7577
7578     /**
7579      * Returns the width for the specified column.
7580      * @param {Number} col The column index
7581      * @return {Number}
7582      */
7583     getColumnWidth : function(col){
7584         return this.config[col].width * 1 || this.defaultWidth;
7585     },
7586
7587     /**
7588      * Sets the width for a column.
7589      * @param {Number} col The column index
7590      * @param {Number} width The new width
7591      */
7592     setColumnWidth : function(col, width, suppressEvent){
7593         this.config[col].width = width;
7594         this.totalWidth = null;
7595         if(!suppressEvent){
7596              this.fireEvent("widthchange", this, col, width);
7597         }
7598     },
7599
7600     /**
7601      * Returns the total width of all columns.
7602      * @param {Boolean} includeHidden True to include hidden column widths
7603      * @return {Number}
7604      */
7605     getTotalWidth : function(includeHidden){
7606         if(!this.totalWidth){
7607             this.totalWidth = 0;
7608             for(var i = 0, len = this.config.length; i < len; i++){
7609                 if(includeHidden || !this.isHidden(i)){
7610                     this.totalWidth += this.getColumnWidth(i);
7611                 }
7612             }
7613         }
7614         return this.totalWidth;
7615     },
7616
7617     /**
7618      * Returns the header for the specified column.
7619      * @param {Number} col The column index
7620      * @return {String}
7621      */
7622     getColumnHeader : function(col){
7623         return this.config[col].header;
7624     },
7625
7626     /**
7627      * Sets the header for a column.
7628      * @param {Number} col The column index
7629      * @param {String} header The new header
7630      */
7631     setColumnHeader : function(col, header){
7632         this.config[col].header = header;
7633         this.fireEvent("headerchange", this, col, header);
7634     },
7635
7636     /**
7637      * Returns the tooltip for the specified column.
7638      * @param {Number} col The column index
7639      * @return {String}
7640      */
7641     getColumnTooltip : function(col){
7642             return this.config[col].tooltip;
7643     },
7644     /**
7645      * Sets the tooltip for a column.
7646      * @param {Number} col The column index
7647      * @param {String} tooltip The new tooltip
7648      */
7649     setColumnTooltip : function(col, tooltip){
7650             this.config[col].tooltip = tooltip;
7651     },
7652
7653     /**
7654      * Returns the dataIndex for the specified column.
7655      * @param {Number} col The column index
7656      * @return {Number}
7657      */
7658     getDataIndex : function(col){
7659         return this.config[col].dataIndex;
7660     },
7661
7662     /**
7663      * Sets the dataIndex for a column.
7664      * @param {Number} col The column index
7665      * @param {Number} dataIndex The new dataIndex
7666      */
7667     setDataIndex : function(col, dataIndex){
7668         this.config[col].dataIndex = dataIndex;
7669     },
7670
7671     
7672     
7673     /**
7674      * Returns true if the cell is editable.
7675      * @param {Number} colIndex The column index
7676      * @param {Number} rowIndex The row index - this is nto actually used..?
7677      * @return {Boolean}
7678      */
7679     isCellEditable : function(colIndex, rowIndex){
7680         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7681     },
7682
7683     /**
7684      * Returns the editor defined for the cell/column.
7685      * return false or null to disable editing.
7686      * @param {Number} colIndex The column index
7687      * @param {Number} rowIndex The row index
7688      * @return {Object}
7689      */
7690     getCellEditor : function(colIndex, rowIndex){
7691         return this.config[colIndex].editor;
7692     },
7693
7694     /**
7695      * Sets if a column is editable.
7696      * @param {Number} col The column index
7697      * @param {Boolean} editable True if the column is editable
7698      */
7699     setEditable : function(col, editable){
7700         this.config[col].editable = editable;
7701     },
7702
7703
7704     /**
7705      * Returns true if the column is hidden.
7706      * @param {Number} colIndex The column index
7707      * @return {Boolean}
7708      */
7709     isHidden : function(colIndex){
7710         return this.config[colIndex].hidden;
7711     },
7712
7713
7714     /**
7715      * Returns true if the column width cannot be changed
7716      */
7717     isFixed : function(colIndex){
7718         return this.config[colIndex].fixed;
7719     },
7720
7721     /**
7722      * Returns true if the column can be resized
7723      * @return {Boolean}
7724      */
7725     isResizable : function(colIndex){
7726         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7727     },
7728     /**
7729      * Sets if a column is hidden.
7730      * @param {Number} colIndex The column index
7731      * @param {Boolean} hidden True if the column is hidden
7732      */
7733     setHidden : function(colIndex, hidden){
7734         this.config[colIndex].hidden = hidden;
7735         this.totalWidth = null;
7736         this.fireEvent("hiddenchange", this, colIndex, hidden);
7737     },
7738
7739     /**
7740      * Sets the editor for a column.
7741      * @param {Number} col The column index
7742      * @param {Object} editor The editor object
7743      */
7744     setEditor : function(col, editor){
7745         this.config[col].editor = editor;
7746     }
7747 });
7748
7749 Roo.grid.ColumnModel.defaultRenderer = function(value)
7750 {
7751     if(typeof value == "object") {
7752         return value;
7753     }
7754         if(typeof value == "string" && value.length < 1){
7755             return "&#160;";
7756         }
7757     
7758         return String.format("{0}", value);
7759 };
7760
7761 // Alias for backwards compatibility
7762 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7763 /*
7764  * Based on:
7765  * Ext JS Library 1.1.1
7766  * Copyright(c) 2006-2007, Ext JS, LLC.
7767  *
7768  * Originally Released Under LGPL - original licence link has changed is not relivant.
7769  *
7770  * Fork - LGPL
7771  * <script type="text/javascript">
7772  */
7773  
7774 /**
7775  * @class Roo.LoadMask
7776  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7777  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7778  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7779  * element's UpdateManager load indicator and will be destroyed after the initial load.
7780  * @constructor
7781  * Create a new LoadMask
7782  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7783  * @param {Object} config The config object
7784  */
7785 Roo.LoadMask = function(el, config){
7786     this.el = Roo.get(el);
7787     Roo.apply(this, config);
7788     if(this.store){
7789         this.store.on('beforeload', this.onBeforeLoad, this);
7790         this.store.on('load', this.onLoad, this);
7791         this.store.on('loadexception', this.onLoadException, this);
7792         this.removeMask = false;
7793     }else{
7794         var um = this.el.getUpdateManager();
7795         um.showLoadIndicator = false; // disable the default indicator
7796         um.on('beforeupdate', this.onBeforeLoad, this);
7797         um.on('update', this.onLoad, this);
7798         um.on('failure', this.onLoad, this);
7799         this.removeMask = true;
7800     }
7801 };
7802
7803 Roo.LoadMask.prototype = {
7804     /**
7805      * @cfg {Boolean} removeMask
7806      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7807      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7808      */
7809     /**
7810      * @cfg {String} msg
7811      * The text to display in a centered loading message box (defaults to 'Loading...')
7812      */
7813     msg : 'Loading...',
7814     /**
7815      * @cfg {String} msgCls
7816      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7817      */
7818     msgCls : 'x-mask-loading',
7819
7820     /**
7821      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7822      * @type Boolean
7823      */
7824     disabled: false,
7825
7826     /**
7827      * Disables the mask to prevent it from being displayed
7828      */
7829     disable : function(){
7830        this.disabled = true;
7831     },
7832
7833     /**
7834      * Enables the mask so that it can be displayed
7835      */
7836     enable : function(){
7837         this.disabled = false;
7838     },
7839     
7840     onLoadException : function()
7841     {
7842         Roo.log(arguments);
7843         
7844         if (typeof(arguments[3]) != 'undefined') {
7845             Roo.MessageBox.alert("Error loading",arguments[3]);
7846         } 
7847         /*
7848         try {
7849             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7850                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7851             }   
7852         } catch(e) {
7853             
7854         }
7855         */
7856     
7857         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7858     },
7859     // private
7860     onLoad : function()
7861     {
7862         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7863     },
7864
7865     // private
7866     onBeforeLoad : function(){
7867         if(!this.disabled){
7868             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7869         }
7870     },
7871
7872     // private
7873     destroy : function(){
7874         if(this.store){
7875             this.store.un('beforeload', this.onBeforeLoad, this);
7876             this.store.un('load', this.onLoad, this);
7877             this.store.un('loadexception', this.onLoadException, this);
7878         }else{
7879             var um = this.el.getUpdateManager();
7880             um.un('beforeupdate', this.onBeforeLoad, this);
7881             um.un('update', this.onLoad, this);
7882             um.un('failure', this.onLoad, this);
7883         }
7884     }
7885 };/*
7886  * - LGPL
7887  *
7888  * table
7889  * 
7890  */
7891
7892 /**
7893  * @class Roo.bootstrap.Table
7894  * @extends Roo.bootstrap.Component
7895  * Bootstrap Table class
7896  * @cfg {String} cls table class
7897  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7898  * @cfg {String} bgcolor Specifies the background color for a table
7899  * @cfg {Number} border Specifies whether the table cells should have borders or not
7900  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7901  * @cfg {Number} cellspacing Specifies the space between cells
7902  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7903  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7904  * @cfg {String} sortable Specifies that the table should be sortable
7905  * @cfg {String} summary Specifies a summary of the content of a table
7906  * @cfg {Number} width Specifies the width of a table
7907  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7908  * 
7909  * @cfg {boolean} striped Should the rows be alternative striped
7910  * @cfg {boolean} bordered Add borders to the table
7911  * @cfg {boolean} hover Add hover highlighting
7912  * @cfg {boolean} condensed Format condensed
7913  * @cfg {boolean} responsive Format condensed
7914  * @cfg {Boolean} loadMask (true|false) default false
7915  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7916  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7917  * @cfg {Boolean} rowSelection (true|false) default false
7918  * @cfg {Boolean} cellSelection (true|false) default false
7919  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7920  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7921  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7922  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7923  
7924  * 
7925  * @constructor
7926  * Create a new Table
7927  * @param {Object} config The config object
7928  */
7929
7930 Roo.bootstrap.Table = function(config){
7931     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7932     
7933   
7934     
7935     // BC...
7936     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7937     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7938     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7939     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7940     
7941     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7942     if (this.sm) {
7943         this.sm.grid = this;
7944         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7945         this.sm = this.selModel;
7946         this.sm.xmodule = this.xmodule || false;
7947     }
7948     
7949     if (this.cm && typeof(this.cm.config) == 'undefined') {
7950         this.colModel = new Roo.grid.ColumnModel(this.cm);
7951         this.cm = this.colModel;
7952         this.cm.xmodule = this.xmodule || false;
7953     }
7954     if (this.store) {
7955         this.store= Roo.factory(this.store, Roo.data);
7956         this.ds = this.store;
7957         this.ds.xmodule = this.xmodule || false;
7958          
7959     }
7960     if (this.footer && this.store) {
7961         this.footer.dataSource = this.ds;
7962         this.footer = Roo.factory(this.footer);
7963     }
7964     
7965     /** @private */
7966     this.addEvents({
7967         /**
7968          * @event cellclick
7969          * Fires when a cell is clicked
7970          * @param {Roo.bootstrap.Table} this
7971          * @param {Roo.Element} el
7972          * @param {Number} rowIndex
7973          * @param {Number} columnIndex
7974          * @param {Roo.EventObject} e
7975          */
7976         "cellclick" : true,
7977         /**
7978          * @event celldblclick
7979          * Fires when a cell is double clicked
7980          * @param {Roo.bootstrap.Table} this
7981          * @param {Roo.Element} el
7982          * @param {Number} rowIndex
7983          * @param {Number} columnIndex
7984          * @param {Roo.EventObject} e
7985          */
7986         "celldblclick" : true,
7987         /**
7988          * @event rowclick
7989          * Fires when a row is clicked
7990          * @param {Roo.bootstrap.Table} this
7991          * @param {Roo.Element} el
7992          * @param {Number} rowIndex
7993          * @param {Roo.EventObject} e
7994          */
7995         "rowclick" : true,
7996         /**
7997          * @event rowdblclick
7998          * Fires when a row is double clicked
7999          * @param {Roo.bootstrap.Table} this
8000          * @param {Roo.Element} el
8001          * @param {Number} rowIndex
8002          * @param {Roo.EventObject} e
8003          */
8004         "rowdblclick" : true,
8005         /**
8006          * @event mouseover
8007          * Fires when a mouseover occur
8008          * @param {Roo.bootstrap.Table} this
8009          * @param {Roo.Element} el
8010          * @param {Number} rowIndex
8011          * @param {Number} columnIndex
8012          * @param {Roo.EventObject} e
8013          */
8014         "mouseover" : true,
8015         /**
8016          * @event mouseout
8017          * Fires when a mouseout occur
8018          * @param {Roo.bootstrap.Table} this
8019          * @param {Roo.Element} el
8020          * @param {Number} rowIndex
8021          * @param {Number} columnIndex
8022          * @param {Roo.EventObject} e
8023          */
8024         "mouseout" : true,
8025         /**
8026          * @event rowclass
8027          * Fires when a row is rendered, so you can change add a style to it.
8028          * @param {Roo.bootstrap.Table} this
8029          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8030          */
8031         'rowclass' : true,
8032           /**
8033          * @event rowsrendered
8034          * Fires when all the  rows have been rendered
8035          * @param {Roo.bootstrap.Table} this
8036          */
8037         'rowsrendered' : true,
8038         /**
8039          * @event contextmenu
8040          * The raw contextmenu event for the entire grid.
8041          * @param {Roo.EventObject} e
8042          */
8043         "contextmenu" : true,
8044         /**
8045          * @event rowcontextmenu
8046          * Fires when a row is right clicked
8047          * @param {Roo.bootstrap.Table} this
8048          * @param {Number} rowIndex
8049          * @param {Roo.EventObject} e
8050          */
8051         "rowcontextmenu" : true,
8052         /**
8053          * @event cellcontextmenu
8054          * Fires when a cell is right clicked
8055          * @param {Roo.bootstrap.Table} this
8056          * @param {Number} rowIndex
8057          * @param {Number} cellIndex
8058          * @param {Roo.EventObject} e
8059          */
8060          "cellcontextmenu" : true,
8061          /**
8062          * @event headercontextmenu
8063          * Fires when a header is right clicked
8064          * @param {Roo.bootstrap.Table} this
8065          * @param {Number} columnIndex
8066          * @param {Roo.EventObject} e
8067          */
8068         "headercontextmenu" : true
8069     });
8070 };
8071
8072 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8073     
8074     cls: false,
8075     align: false,
8076     bgcolor: false,
8077     border: false,
8078     cellpadding: false,
8079     cellspacing: false,
8080     frame: false,
8081     rules: false,
8082     sortable: false,
8083     summary: false,
8084     width: false,
8085     striped : false,
8086     scrollBody : false,
8087     bordered: false,
8088     hover:  false,
8089     condensed : false,
8090     responsive : false,
8091     sm : false,
8092     cm : false,
8093     store : false,
8094     loadMask : false,
8095     footerShow : true,
8096     headerShow : true,
8097   
8098     rowSelection : false,
8099     cellSelection : false,
8100     layout : false,
8101     
8102     // Roo.Element - the tbody
8103     mainBody: false,
8104     // Roo.Element - thead element
8105     mainHead: false,
8106     
8107     container: false, // used by gridpanel...
8108     
8109     lazyLoad : false,
8110     
8111     CSS : Roo.util.CSS,
8112     
8113     auto_hide_footer : false,
8114     
8115     getAutoCreate : function()
8116     {
8117         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8118         
8119         cfg = {
8120             tag: 'table',
8121             cls : 'table',
8122             cn : []
8123         };
8124         if (this.scrollBody) {
8125             cfg.cls += ' table-body-fixed';
8126         }    
8127         if (this.striped) {
8128             cfg.cls += ' table-striped';
8129         }
8130         
8131         if (this.hover) {
8132             cfg.cls += ' table-hover';
8133         }
8134         if (this.bordered) {
8135             cfg.cls += ' table-bordered';
8136         }
8137         if (this.condensed) {
8138             cfg.cls += ' table-condensed';
8139         }
8140         if (this.responsive) {
8141             cfg.cls += ' table-responsive';
8142         }
8143         
8144         if (this.cls) {
8145             cfg.cls+=  ' ' +this.cls;
8146         }
8147         
8148         // this lot should be simplifed...
8149         var _t = this;
8150         var cp = [
8151             'align',
8152             'bgcolor',
8153             'border',
8154             'cellpadding',
8155             'cellspacing',
8156             'frame',
8157             'rules',
8158             'sortable',
8159             'summary',
8160             'width'
8161         ].forEach(function(k) {
8162             if (_t[k]) {
8163                 cfg[k] = _t[k];
8164             }
8165         });
8166         
8167         
8168         if (this.layout) {
8169             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8170         }
8171         
8172         if(this.store || this.cm){
8173             if(this.headerShow){
8174                 cfg.cn.push(this.renderHeader());
8175             }
8176             
8177             cfg.cn.push(this.renderBody());
8178             
8179             if(this.footerShow){
8180                 cfg.cn.push(this.renderFooter());
8181             }
8182             // where does this come from?
8183             //cfg.cls+=  ' TableGrid';
8184         }
8185         
8186         return { cn : [ cfg ] };
8187     },
8188     
8189     initEvents : function()
8190     {   
8191         if(!this.store || !this.cm){
8192             return;
8193         }
8194         if (this.selModel) {
8195             this.selModel.initEvents();
8196         }
8197         
8198         
8199         //Roo.log('initEvents with ds!!!!');
8200         
8201         this.mainBody = this.el.select('tbody', true).first();
8202         this.mainHead = this.el.select('thead', true).first();
8203         this.mainFoot = this.el.select('tfoot', true).first();
8204         
8205         
8206         
8207         var _this = this;
8208         
8209         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8210             e.on('click', _this.sort, _this);
8211         });
8212         
8213         this.mainBody.on("click", this.onClick, this);
8214         this.mainBody.on("dblclick", this.onDblClick, this);
8215         
8216         // why is this done????? = it breaks dialogs??
8217         //this.parent().el.setStyle('position', 'relative');
8218         
8219         
8220         if (this.footer) {
8221             this.footer.parentId = this.id;
8222             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8223             
8224             if(this.lazyLoad){
8225                 this.el.select('tfoot tr td').first().addClass('hide');
8226             }
8227         } 
8228         
8229         if(this.loadMask) {
8230             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8231         }
8232         
8233         this.store.on('load', this.onLoad, this);
8234         this.store.on('beforeload', this.onBeforeLoad, this);
8235         this.store.on('update', this.onUpdate, this);
8236         this.store.on('add', this.onAdd, this);
8237         this.store.on("clear", this.clear, this);
8238         
8239         this.el.on("contextmenu", this.onContextMenu, this);
8240         
8241         this.mainBody.on('scroll', this.onBodyScroll, this);
8242         
8243         this.cm.on("headerchange", this.onHeaderChange, this);
8244         
8245         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8246         
8247     },
8248     
8249     onContextMenu : function(e, t)
8250     {
8251         this.processEvent("contextmenu", e);
8252     },
8253     
8254     processEvent : function(name, e)
8255     {
8256         if (name != 'touchstart' ) {
8257             this.fireEvent(name, e);    
8258         }
8259         
8260         var t = e.getTarget();
8261         
8262         var cell = Roo.get(t);
8263         
8264         if(!cell){
8265             return;
8266         }
8267         
8268         if(cell.findParent('tfoot', false, true)){
8269             return;
8270         }
8271         
8272         if(cell.findParent('thead', false, true)){
8273             
8274             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8275                 cell = Roo.get(t).findParent('th', false, true);
8276                 if (!cell) {
8277                     Roo.log("failed to find th in thead?");
8278                     Roo.log(e.getTarget());
8279                     return;
8280                 }
8281             }
8282             
8283             var cellIndex = cell.dom.cellIndex;
8284             
8285             var ename = name == 'touchstart' ? 'click' : name;
8286             this.fireEvent("header" + ename, this, cellIndex, e);
8287             
8288             return;
8289         }
8290         
8291         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8292             cell = Roo.get(t).findParent('td', false, true);
8293             if (!cell) {
8294                 Roo.log("failed to find th in tbody?");
8295                 Roo.log(e.getTarget());
8296                 return;
8297             }
8298         }
8299         
8300         var row = cell.findParent('tr', false, true);
8301         var cellIndex = cell.dom.cellIndex;
8302         var rowIndex = row.dom.rowIndex - 1;
8303         
8304         if(row !== false){
8305             
8306             this.fireEvent("row" + name, this, rowIndex, e);
8307             
8308             if(cell !== false){
8309             
8310                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8311             }
8312         }
8313         
8314     },
8315     
8316     onMouseover : function(e, el)
8317     {
8318         var cell = Roo.get(el);
8319         
8320         if(!cell){
8321             return;
8322         }
8323         
8324         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8325             cell = cell.findParent('td', false, true);
8326         }
8327         
8328         var row = cell.findParent('tr', false, true);
8329         var cellIndex = cell.dom.cellIndex;
8330         var rowIndex = row.dom.rowIndex - 1; // start from 0
8331         
8332         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8333         
8334     },
8335     
8336     onMouseout : function(e, el)
8337     {
8338         var cell = Roo.get(el);
8339         
8340         if(!cell){
8341             return;
8342         }
8343         
8344         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8345             cell = cell.findParent('td', false, true);
8346         }
8347         
8348         var row = cell.findParent('tr', false, true);
8349         var cellIndex = cell.dom.cellIndex;
8350         var rowIndex = row.dom.rowIndex - 1; // start from 0
8351         
8352         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8353         
8354     },
8355     
8356     onClick : function(e, el)
8357     {
8358         var cell = Roo.get(el);
8359         
8360         if(!cell || (!this.cellSelection && !this.rowSelection)){
8361             return;
8362         }
8363         
8364         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8365             cell = cell.findParent('td', false, true);
8366         }
8367         
8368         if(!cell || typeof(cell) == 'undefined'){
8369             return;
8370         }
8371         
8372         var row = cell.findParent('tr', false, true);
8373         
8374         if(!row || typeof(row) == 'undefined'){
8375             return;
8376         }
8377         
8378         var cellIndex = cell.dom.cellIndex;
8379         var rowIndex = this.getRowIndex(row);
8380         
8381         // why??? - should these not be based on SelectionModel?
8382         if(this.cellSelection){
8383             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8384         }
8385         
8386         if(this.rowSelection){
8387             this.fireEvent('rowclick', this, row, rowIndex, e);
8388         }
8389         
8390         
8391     },
8392         
8393     onDblClick : function(e,el)
8394     {
8395         var cell = Roo.get(el);
8396         
8397         if(!cell || (!this.cellSelection && !this.rowSelection)){
8398             return;
8399         }
8400         
8401         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402             cell = cell.findParent('td', false, true);
8403         }
8404         
8405         if(!cell || typeof(cell) == 'undefined'){
8406             return;
8407         }
8408         
8409         var row = cell.findParent('tr', false, true);
8410         
8411         if(!row || typeof(row) == 'undefined'){
8412             return;
8413         }
8414         
8415         var cellIndex = cell.dom.cellIndex;
8416         var rowIndex = this.getRowIndex(row);
8417         
8418         if(this.cellSelection){
8419             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8420         }
8421         
8422         if(this.rowSelection){
8423             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8424         }
8425     },
8426     
8427     sort : function(e,el)
8428     {
8429         var col = Roo.get(el);
8430         
8431         if(!col.hasClass('sortable')){
8432             return;
8433         }
8434         
8435         var sort = col.attr('sort');
8436         var dir = 'ASC';
8437         
8438         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8439             dir = 'DESC';
8440         }
8441         
8442         this.store.sortInfo = {field : sort, direction : dir};
8443         
8444         if (this.footer) {
8445             Roo.log("calling footer first");
8446             this.footer.onClick('first');
8447         } else {
8448         
8449             this.store.load({ params : { start : 0 } });
8450         }
8451     },
8452     
8453     renderHeader : function()
8454     {
8455         var header = {
8456             tag: 'thead',
8457             cn : []
8458         };
8459         
8460         var cm = this.cm;
8461         this.totalWidth = 0;
8462         
8463         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8464             
8465             var config = cm.config[i];
8466             
8467             var c = {
8468                 tag: 'th',
8469                 cls : 'x-hcol-' + i,
8470                 style : '',
8471                 html: cm.getColumnHeader(i)
8472             };
8473             
8474             var hh = '';
8475             
8476             if(typeof(config.sortable) != 'undefined' && config.sortable){
8477                 c.cls = 'sortable';
8478                 c.html = '<i class="glyphicon"></i>' + c.html;
8479             }
8480             
8481             // could use BS4 hidden-..-down 
8482             
8483             if(typeof(config.lgHeader) != 'undefined'){
8484                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8485             }
8486             
8487             if(typeof(config.mdHeader) != 'undefined'){
8488                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8489             }
8490             
8491             if(typeof(config.smHeader) != 'undefined'){
8492                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8493             }
8494             
8495             if(typeof(config.xsHeader) != 'undefined'){
8496                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8497             }
8498             
8499             if(hh.length){
8500                 c.html = hh;
8501             }
8502             
8503             if(typeof(config.tooltip) != 'undefined'){
8504                 c.tooltip = config.tooltip;
8505             }
8506             
8507             if(typeof(config.colspan) != 'undefined'){
8508                 c.colspan = config.colspan;
8509             }
8510             
8511             if(typeof(config.hidden) != 'undefined' && config.hidden){
8512                 c.style += ' display:none;';
8513             }
8514             
8515             if(typeof(config.dataIndex) != 'undefined'){
8516                 c.sort = config.dataIndex;
8517             }
8518             
8519            
8520             
8521             if(typeof(config.align) != 'undefined' && config.align.length){
8522                 c.style += ' text-align:' + config.align + ';';
8523             }
8524             
8525             if(typeof(config.width) != 'undefined'){
8526                 c.style += ' width:' + config.width + 'px;';
8527                 this.totalWidth += config.width;
8528             } else {
8529                 this.totalWidth += 100; // assume minimum of 100 per column?
8530             }
8531             
8532             if(typeof(config.cls) != 'undefined'){
8533                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8534             }
8535             
8536             ['xs','sm','md','lg'].map(function(size){
8537                 
8538                 if(typeof(config[size]) == 'undefined'){
8539                     return;
8540                 }
8541                  
8542                 if (!config[size]) { // 0 = hidden
8543                     // BS 4 '0' is treated as hide that column and below.
8544                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8545                     return;
8546                 }
8547                 
8548                 c.cls += ' col-' + size + '-' + config[size] + (
8549                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8550                 );
8551                 
8552                 
8553             });
8554             
8555             header.cn.push(c)
8556         }
8557         
8558         return header;
8559     },
8560     
8561     renderBody : function()
8562     {
8563         var body = {
8564             tag: 'tbody',
8565             cn : [
8566                 {
8567                     tag: 'tr',
8568                     cn : [
8569                         {
8570                             tag : 'td',
8571                             colspan :  this.cm.getColumnCount()
8572                         }
8573                     ]
8574                 }
8575             ]
8576         };
8577         
8578         return body;
8579     },
8580     
8581     renderFooter : function()
8582     {
8583         var footer = {
8584             tag: 'tfoot',
8585             cn : [
8586                 {
8587                     tag: 'tr',
8588                     cn : [
8589                         {
8590                             tag : 'td',
8591                             colspan :  this.cm.getColumnCount()
8592                         }
8593                     ]
8594                 }
8595             ]
8596         };
8597         
8598         return footer;
8599     },
8600     
8601     
8602     
8603     onLoad : function()
8604     {
8605 //        Roo.log('ds onload');
8606         this.clear();
8607         
8608         var _this = this;
8609         var cm = this.cm;
8610         var ds = this.store;
8611         
8612         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8613             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8614             if (_this.store.sortInfo) {
8615                     
8616                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8617                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8618                 }
8619                 
8620                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8621                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8622                 }
8623             }
8624         });
8625         
8626         var tbody =  this.mainBody;
8627               
8628         if(ds.getCount() > 0){
8629             ds.data.each(function(d,rowIndex){
8630                 var row =  this.renderRow(cm, ds, rowIndex);
8631                 
8632                 tbody.createChild(row);
8633                 
8634                 var _this = this;
8635                 
8636                 if(row.cellObjects.length){
8637                     Roo.each(row.cellObjects, function(r){
8638                         _this.renderCellObject(r);
8639                     })
8640                 }
8641                 
8642             }, this);
8643         }
8644         
8645         var tfoot = this.el.select('tfoot', true).first();
8646         
8647         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8648             
8649             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8650             
8651             var total = this.ds.getTotalCount();
8652             
8653             if(this.footer.pageSize < total){
8654                 this.mainFoot.show();
8655             }
8656         }
8657         
8658         Roo.each(this.el.select('tbody td', true).elements, function(e){
8659             e.on('mouseover', _this.onMouseover, _this);
8660         });
8661         
8662         Roo.each(this.el.select('tbody td', true).elements, function(e){
8663             e.on('mouseout', _this.onMouseout, _this);
8664         });
8665         this.fireEvent('rowsrendered', this);
8666         
8667         this.autoSize();
8668     },
8669     
8670     
8671     onUpdate : function(ds,record)
8672     {
8673         this.refreshRow(record);
8674         this.autoSize();
8675     },
8676     
8677     onRemove : function(ds, record, index, isUpdate){
8678         if(isUpdate !== true){
8679             this.fireEvent("beforerowremoved", this, index, record);
8680         }
8681         var bt = this.mainBody.dom;
8682         
8683         var rows = this.el.select('tbody > tr', true).elements;
8684         
8685         if(typeof(rows[index]) != 'undefined'){
8686             bt.removeChild(rows[index].dom);
8687         }
8688         
8689 //        if(bt.rows[index]){
8690 //            bt.removeChild(bt.rows[index]);
8691 //        }
8692         
8693         if(isUpdate !== true){
8694             //this.stripeRows(index);
8695             //this.syncRowHeights(index, index);
8696             //this.layout();
8697             this.fireEvent("rowremoved", this, index, record);
8698         }
8699     },
8700     
8701     onAdd : function(ds, records, rowIndex)
8702     {
8703         //Roo.log('on Add called');
8704         // - note this does not handle multiple adding very well..
8705         var bt = this.mainBody.dom;
8706         for (var i =0 ; i < records.length;i++) {
8707             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8708             //Roo.log(records[i]);
8709             //Roo.log(this.store.getAt(rowIndex+i));
8710             this.insertRow(this.store, rowIndex + i, false);
8711             return;
8712         }
8713         
8714     },
8715     
8716     
8717     refreshRow : function(record){
8718         var ds = this.store, index;
8719         if(typeof record == 'number'){
8720             index = record;
8721             record = ds.getAt(index);
8722         }else{
8723             index = ds.indexOf(record);
8724             if (index < 0) {
8725                 return; // should not happen - but seems to 
8726             }
8727         }
8728         this.insertRow(ds, index, true);
8729         this.autoSize();
8730         this.onRemove(ds, record, index+1, true);
8731         this.autoSize();
8732         //this.syncRowHeights(index, index);
8733         //this.layout();
8734         this.fireEvent("rowupdated", this, index, record);
8735     },
8736     
8737     insertRow : function(dm, rowIndex, isUpdate){
8738         
8739         if(!isUpdate){
8740             this.fireEvent("beforerowsinserted", this, rowIndex);
8741         }
8742             //var s = this.getScrollState();
8743         var row = this.renderRow(this.cm, this.store, rowIndex);
8744         // insert before rowIndex..
8745         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8746         
8747         var _this = this;
8748                 
8749         if(row.cellObjects.length){
8750             Roo.each(row.cellObjects, function(r){
8751                 _this.renderCellObject(r);
8752             })
8753         }
8754             
8755         if(!isUpdate){
8756             this.fireEvent("rowsinserted", this, rowIndex);
8757             //this.syncRowHeights(firstRow, lastRow);
8758             //this.stripeRows(firstRow);
8759             //this.layout();
8760         }
8761         
8762     },
8763     
8764     
8765     getRowDom : function(rowIndex)
8766     {
8767         var rows = this.el.select('tbody > tr', true).elements;
8768         
8769         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8770         
8771     },
8772     // returns the object tree for a tr..
8773   
8774     
8775     renderRow : function(cm, ds, rowIndex) 
8776     {
8777         var d = ds.getAt(rowIndex);
8778         
8779         var row = {
8780             tag : 'tr',
8781             cls : 'x-row-' + rowIndex,
8782             cn : []
8783         };
8784             
8785         var cellObjects = [];
8786         
8787         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8788             var config = cm.config[i];
8789             
8790             var renderer = cm.getRenderer(i);
8791             var value = '';
8792             var id = false;
8793             
8794             if(typeof(renderer) !== 'undefined'){
8795                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8796             }
8797             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8798             // and are rendered into the cells after the row is rendered - using the id for the element.
8799             
8800             if(typeof(value) === 'object'){
8801                 id = Roo.id();
8802                 cellObjects.push({
8803                     container : id,
8804                     cfg : value 
8805                 })
8806             }
8807             
8808             var rowcfg = {
8809                 record: d,
8810                 rowIndex : rowIndex,
8811                 colIndex : i,
8812                 rowClass : ''
8813             };
8814
8815             this.fireEvent('rowclass', this, rowcfg);
8816             
8817             var td = {
8818                 tag: 'td',
8819                 cls : rowcfg.rowClass + ' x-col-' + i,
8820                 style: '',
8821                 html: (typeof(value) === 'object') ? '' : value
8822             };
8823             
8824             if (id) {
8825                 td.id = id;
8826             }
8827             
8828             if(typeof(config.colspan) != 'undefined'){
8829                 td.colspan = config.colspan;
8830             }
8831             
8832             if(typeof(config.hidden) != 'undefined' && config.hidden){
8833                 td.style += ' display:none;';
8834             }
8835             
8836             if(typeof(config.align) != 'undefined' && config.align.length){
8837                 td.style += ' text-align:' + config.align + ';';
8838             }
8839             if(typeof(config.valign) != 'undefined' && config.valign.length){
8840                 td.style += ' vertical-align:' + config.valign + ';';
8841             }
8842             
8843             if(typeof(config.width) != 'undefined'){
8844                 td.style += ' width:' +  config.width + 'px;';
8845             }
8846             
8847             if(typeof(config.cursor) != 'undefined'){
8848                 td.style += ' cursor:' +  config.cursor + ';';
8849             }
8850             
8851             if(typeof(config.cls) != 'undefined'){
8852                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8853             }
8854             
8855             ['xs','sm','md','lg'].map(function(size){
8856                 
8857                 if(typeof(config[size]) == 'undefined'){
8858                     return;
8859                 }
8860                 
8861                 
8862                   
8863                 if (!config[size]) { // 0 = hidden
8864                     // BS 4 '0' is treated as hide that column and below.
8865                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8866                     return;
8867                 }
8868                 
8869                 td.cls += ' col-' + size + '-' + config[size] + (
8870                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8871                 );
8872                  
8873
8874             });
8875             
8876             row.cn.push(td);
8877            
8878         }
8879         
8880         row.cellObjects = cellObjects;
8881         
8882         return row;
8883           
8884     },
8885     
8886     
8887     
8888     onBeforeLoad : function()
8889     {
8890         
8891     },
8892      /**
8893      * Remove all rows
8894      */
8895     clear : function()
8896     {
8897         this.el.select('tbody', true).first().dom.innerHTML = '';
8898     },
8899     /**
8900      * Show or hide a row.
8901      * @param {Number} rowIndex to show or hide
8902      * @param {Boolean} state hide
8903      */
8904     setRowVisibility : function(rowIndex, state)
8905     {
8906         var bt = this.mainBody.dom;
8907         
8908         var rows = this.el.select('tbody > tr', true).elements;
8909         
8910         if(typeof(rows[rowIndex]) == 'undefined'){
8911             return;
8912         }
8913         rows[rowIndex].dom.style.display = state ? '' : 'none';
8914     },
8915     
8916     
8917     getSelectionModel : function(){
8918         if(!this.selModel){
8919             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8920         }
8921         return this.selModel;
8922     },
8923     /*
8924      * Render the Roo.bootstrap object from renderder
8925      */
8926     renderCellObject : function(r)
8927     {
8928         var _this = this;
8929         
8930         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8931         
8932         var t = r.cfg.render(r.container);
8933         
8934         if(r.cfg.cn){
8935             Roo.each(r.cfg.cn, function(c){
8936                 var child = {
8937                     container: t.getChildContainer(),
8938                     cfg: c
8939                 };
8940                 _this.renderCellObject(child);
8941             })
8942         }
8943     },
8944     
8945     getRowIndex : function(row)
8946     {
8947         var rowIndex = -1;
8948         
8949         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8950             if(el != row){
8951                 return;
8952             }
8953             
8954             rowIndex = index;
8955         });
8956         
8957         return rowIndex;
8958     },
8959      /**
8960      * Returns the grid's underlying element = used by panel.Grid
8961      * @return {Element} The element
8962      */
8963     getGridEl : function(){
8964         return this.el;
8965     },
8966      /**
8967      * Forces a resize - used by panel.Grid
8968      * @return {Element} The element
8969      */
8970     autoSize : function()
8971     {
8972         //var ctr = Roo.get(this.container.dom.parentElement);
8973         var ctr = Roo.get(this.el.dom);
8974         
8975         var thd = this.getGridEl().select('thead',true).first();
8976         var tbd = this.getGridEl().select('tbody', true).first();
8977         var tfd = this.getGridEl().select('tfoot', true).first();
8978         
8979         var cw = ctr.getWidth();
8980         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8981         
8982         if (tbd) {
8983             
8984             tbd.setWidth(ctr.getWidth());
8985             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8986             // this needs fixing for various usage - currently only hydra job advers I think..
8987             //tdb.setHeight(
8988             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8989             //); 
8990             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8991             cw -= barsize;
8992         }
8993         cw = Math.max(cw, this.totalWidth);
8994         this.getGridEl().select('tbody tr',true).setWidth(cw);
8995         
8996         // resize 'expandable coloumn?
8997         
8998         return; // we doe not have a view in this design..
8999         
9000     },
9001     onBodyScroll: function()
9002     {
9003         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9004         if(this.mainHead){
9005             this.mainHead.setStyle({
9006                 'position' : 'relative',
9007                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9008             });
9009         }
9010         
9011         if(this.lazyLoad){
9012             
9013             var scrollHeight = this.mainBody.dom.scrollHeight;
9014             
9015             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9016             
9017             var height = this.mainBody.getHeight();
9018             
9019             if(scrollHeight - height == scrollTop) {
9020                 
9021                 var total = this.ds.getTotalCount();
9022                 
9023                 if(this.footer.cursor + this.footer.pageSize < total){
9024                     
9025                     this.footer.ds.load({
9026                         params : {
9027                             start : this.footer.cursor + this.footer.pageSize,
9028                             limit : this.footer.pageSize
9029                         },
9030                         add : true
9031                     });
9032                 }
9033             }
9034             
9035         }
9036     },
9037     
9038     onHeaderChange : function()
9039     {
9040         var header = this.renderHeader();
9041         var table = this.el.select('table', true).first();
9042         
9043         this.mainHead.remove();
9044         this.mainHead = table.createChild(header, this.mainBody, false);
9045     },
9046     
9047     onHiddenChange : function(colModel, colIndex, hidden)
9048     {
9049         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9050         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9051         
9052         this.CSS.updateRule(thSelector, "display", "");
9053         this.CSS.updateRule(tdSelector, "display", "");
9054         
9055         if(hidden){
9056             this.CSS.updateRule(thSelector, "display", "none");
9057             this.CSS.updateRule(tdSelector, "display", "none");
9058         }
9059         
9060         this.onHeaderChange();
9061         this.onLoad();
9062     },
9063     
9064     setColumnWidth: function(col_index, width)
9065     {
9066         // width = "md-2 xs-2..."
9067         if(!this.colModel.config[col_index]) {
9068             return;
9069         }
9070         
9071         var w = width.split(" ");
9072         
9073         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9074         
9075         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9076         
9077         
9078         for(var j = 0; j < w.length; j++) {
9079             
9080             if(!w[j]) {
9081                 continue;
9082             }
9083             
9084             var size_cls = w[j].split("-");
9085             
9086             if(!Number.isInteger(size_cls[1] * 1)) {
9087                 continue;
9088             }
9089             
9090             if(!this.colModel.config[col_index][size_cls[0]]) {
9091                 continue;
9092             }
9093             
9094             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9095                 continue;
9096             }
9097             
9098             h_row[0].classList.replace(
9099                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9100                 "col-"+size_cls[0]+"-"+size_cls[1]
9101             );
9102             
9103             for(var i = 0; i < rows.length; i++) {
9104                 
9105                 var size_cls = w[j].split("-");
9106                 
9107                 if(!Number.isInteger(size_cls[1] * 1)) {
9108                     continue;
9109                 }
9110                 
9111                 if(!this.colModel.config[col_index][size_cls[0]]) {
9112                     continue;
9113                 }
9114                 
9115                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9116                     continue;
9117                 }
9118                 
9119                 rows[i].classList.replace(
9120                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9121                     "col-"+size_cls[0]+"-"+size_cls[1]
9122                 );
9123             }
9124             
9125             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9126         }
9127     }
9128 });
9129
9130  
9131
9132  /*
9133  * - LGPL
9134  *
9135  * table cell
9136  * 
9137  */
9138
9139 /**
9140  * @class Roo.bootstrap.TableCell
9141  * @extends Roo.bootstrap.Component
9142  * Bootstrap TableCell class
9143  * @cfg {String} html cell contain text
9144  * @cfg {String} cls cell class
9145  * @cfg {String} tag cell tag (td|th) default td
9146  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9147  * @cfg {String} align Aligns the content in a cell
9148  * @cfg {String} axis Categorizes cells
9149  * @cfg {String} bgcolor Specifies the background color of a cell
9150  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9151  * @cfg {Number} colspan Specifies the number of columns a cell should span
9152  * @cfg {String} headers Specifies one or more header cells a cell is related to
9153  * @cfg {Number} height Sets the height of a cell
9154  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9155  * @cfg {Number} rowspan Sets the number of rows a cell should span
9156  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9157  * @cfg {String} valign Vertical aligns the content in a cell
9158  * @cfg {Number} width Specifies the width of a cell
9159  * 
9160  * @constructor
9161  * Create a new TableCell
9162  * @param {Object} config The config object
9163  */
9164
9165 Roo.bootstrap.TableCell = function(config){
9166     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9167 };
9168
9169 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9170     
9171     html: false,
9172     cls: false,
9173     tag: false,
9174     abbr: false,
9175     align: false,
9176     axis: false,
9177     bgcolor: false,
9178     charoff: false,
9179     colspan: false,
9180     headers: false,
9181     height: false,
9182     nowrap: false,
9183     rowspan: false,
9184     scope: false,
9185     valign: false,
9186     width: false,
9187     
9188     
9189     getAutoCreate : function(){
9190         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9191         
9192         cfg = {
9193             tag: 'td'
9194         };
9195         
9196         if(this.tag){
9197             cfg.tag = this.tag;
9198         }
9199         
9200         if (this.html) {
9201             cfg.html=this.html
9202         }
9203         if (this.cls) {
9204             cfg.cls=this.cls
9205         }
9206         if (this.abbr) {
9207             cfg.abbr=this.abbr
9208         }
9209         if (this.align) {
9210             cfg.align=this.align
9211         }
9212         if (this.axis) {
9213             cfg.axis=this.axis
9214         }
9215         if (this.bgcolor) {
9216             cfg.bgcolor=this.bgcolor
9217         }
9218         if (this.charoff) {
9219             cfg.charoff=this.charoff
9220         }
9221         if (this.colspan) {
9222             cfg.colspan=this.colspan
9223         }
9224         if (this.headers) {
9225             cfg.headers=this.headers
9226         }
9227         if (this.height) {
9228             cfg.height=this.height
9229         }
9230         if (this.nowrap) {
9231             cfg.nowrap=this.nowrap
9232         }
9233         if (this.rowspan) {
9234             cfg.rowspan=this.rowspan
9235         }
9236         if (this.scope) {
9237             cfg.scope=this.scope
9238         }
9239         if (this.valign) {
9240             cfg.valign=this.valign
9241         }
9242         if (this.width) {
9243             cfg.width=this.width
9244         }
9245         
9246         
9247         return cfg;
9248     }
9249    
9250 });
9251
9252  
9253
9254  /*
9255  * - LGPL
9256  *
9257  * table row
9258  * 
9259  */
9260
9261 /**
9262  * @class Roo.bootstrap.TableRow
9263  * @extends Roo.bootstrap.Component
9264  * Bootstrap TableRow class
9265  * @cfg {String} cls row class
9266  * @cfg {String} align Aligns the content in a table row
9267  * @cfg {String} bgcolor Specifies a background color for a table row
9268  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9269  * @cfg {String} valign Vertical aligns the content in a table row
9270  * 
9271  * @constructor
9272  * Create a new TableRow
9273  * @param {Object} config The config object
9274  */
9275
9276 Roo.bootstrap.TableRow = function(config){
9277     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9278 };
9279
9280 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9281     
9282     cls: false,
9283     align: false,
9284     bgcolor: false,
9285     charoff: false,
9286     valign: false,
9287     
9288     getAutoCreate : function(){
9289         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9290         
9291         cfg = {
9292             tag: 'tr'
9293         };
9294             
9295         if(this.cls){
9296             cfg.cls = this.cls;
9297         }
9298         if(this.align){
9299             cfg.align = this.align;
9300         }
9301         if(this.bgcolor){
9302             cfg.bgcolor = this.bgcolor;
9303         }
9304         if(this.charoff){
9305             cfg.charoff = this.charoff;
9306         }
9307         if(this.valign){
9308             cfg.valign = this.valign;
9309         }
9310         
9311         return cfg;
9312     }
9313    
9314 });
9315
9316  
9317
9318  /*
9319  * - LGPL
9320  *
9321  * table body
9322  * 
9323  */
9324
9325 /**
9326  * @class Roo.bootstrap.TableBody
9327  * @extends Roo.bootstrap.Component
9328  * Bootstrap TableBody class
9329  * @cfg {String} cls element class
9330  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9331  * @cfg {String} align Aligns the content inside the element
9332  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9333  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9334  * 
9335  * @constructor
9336  * Create a new TableBody
9337  * @param {Object} config The config object
9338  */
9339
9340 Roo.bootstrap.TableBody = function(config){
9341     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9342 };
9343
9344 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9345     
9346     cls: false,
9347     tag: false,
9348     align: false,
9349     charoff: false,
9350     valign: false,
9351     
9352     getAutoCreate : function(){
9353         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9354         
9355         cfg = {
9356             tag: 'tbody'
9357         };
9358             
9359         if (this.cls) {
9360             cfg.cls=this.cls
9361         }
9362         if(this.tag){
9363             cfg.tag = this.tag;
9364         }
9365         
9366         if(this.align){
9367             cfg.align = this.align;
9368         }
9369         if(this.charoff){
9370             cfg.charoff = this.charoff;
9371         }
9372         if(this.valign){
9373             cfg.valign = this.valign;
9374         }
9375         
9376         return cfg;
9377     }
9378     
9379     
9380 //    initEvents : function()
9381 //    {
9382 //        
9383 //        if(!this.store){
9384 //            return;
9385 //        }
9386 //        
9387 //        this.store = Roo.factory(this.store, Roo.data);
9388 //        this.store.on('load', this.onLoad, this);
9389 //        
9390 //        this.store.load();
9391 //        
9392 //    },
9393 //    
9394 //    onLoad: function () 
9395 //    {   
9396 //        this.fireEvent('load', this);
9397 //    }
9398 //    
9399 //   
9400 });
9401
9402  
9403
9404  /*
9405  * Based on:
9406  * Ext JS Library 1.1.1
9407  * Copyright(c) 2006-2007, Ext JS, LLC.
9408  *
9409  * Originally Released Under LGPL - original licence link has changed is not relivant.
9410  *
9411  * Fork - LGPL
9412  * <script type="text/javascript">
9413  */
9414
9415 // as we use this in bootstrap.
9416 Roo.namespace('Roo.form');
9417  /**
9418  * @class Roo.form.Action
9419  * Internal Class used to handle form actions
9420  * @constructor
9421  * @param {Roo.form.BasicForm} el The form element or its id
9422  * @param {Object} config Configuration options
9423  */
9424
9425  
9426  
9427 // define the action interface
9428 Roo.form.Action = function(form, options){
9429     this.form = form;
9430     this.options = options || {};
9431 };
9432 /**
9433  * Client Validation Failed
9434  * @const 
9435  */
9436 Roo.form.Action.CLIENT_INVALID = 'client';
9437 /**
9438  * Server Validation Failed
9439  * @const 
9440  */
9441 Roo.form.Action.SERVER_INVALID = 'server';
9442  /**
9443  * Connect to Server Failed
9444  * @const 
9445  */
9446 Roo.form.Action.CONNECT_FAILURE = 'connect';
9447 /**
9448  * Reading Data from Server Failed
9449  * @const 
9450  */
9451 Roo.form.Action.LOAD_FAILURE = 'load';
9452
9453 Roo.form.Action.prototype = {
9454     type : 'default',
9455     failureType : undefined,
9456     response : undefined,
9457     result : undefined,
9458
9459     // interface method
9460     run : function(options){
9461
9462     },
9463
9464     // interface method
9465     success : function(response){
9466
9467     },
9468
9469     // interface method
9470     handleResponse : function(response){
9471
9472     },
9473
9474     // default connection failure
9475     failure : function(response){
9476         
9477         this.response = response;
9478         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9479         this.form.afterAction(this, false);
9480     },
9481
9482     processResponse : function(response){
9483         this.response = response;
9484         if(!response.responseText){
9485             return true;
9486         }
9487         this.result = this.handleResponse(response);
9488         return this.result;
9489     },
9490
9491     // utility functions used internally
9492     getUrl : function(appendParams){
9493         var url = this.options.url || this.form.url || this.form.el.dom.action;
9494         if(appendParams){
9495             var p = this.getParams();
9496             if(p){
9497                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9498             }
9499         }
9500         return url;
9501     },
9502
9503     getMethod : function(){
9504         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9505     },
9506
9507     getParams : function(){
9508         var bp = this.form.baseParams;
9509         var p = this.options.params;
9510         if(p){
9511             if(typeof p == "object"){
9512                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9513             }else if(typeof p == 'string' && bp){
9514                 p += '&' + Roo.urlEncode(bp);
9515             }
9516         }else if(bp){
9517             p = Roo.urlEncode(bp);
9518         }
9519         return p;
9520     },
9521
9522     createCallback : function(){
9523         return {
9524             success: this.success,
9525             failure: this.failure,
9526             scope: this,
9527             timeout: (this.form.timeout*1000),
9528             upload: this.form.fileUpload ? this.success : undefined
9529         };
9530     }
9531 };
9532
9533 Roo.form.Action.Submit = function(form, options){
9534     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9535 };
9536
9537 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9538     type : 'submit',
9539
9540     haveProgress : false,
9541     uploadComplete : false,
9542     
9543     // uploadProgress indicator.
9544     uploadProgress : function()
9545     {
9546         if (!this.form.progressUrl) {
9547             return;
9548         }
9549         
9550         if (!this.haveProgress) {
9551             Roo.MessageBox.progress("Uploading", "Uploading");
9552         }
9553         if (this.uploadComplete) {
9554            Roo.MessageBox.hide();
9555            return;
9556         }
9557         
9558         this.haveProgress = true;
9559    
9560         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9561         
9562         var c = new Roo.data.Connection();
9563         c.request({
9564             url : this.form.progressUrl,
9565             params: {
9566                 id : uid
9567             },
9568             method: 'GET',
9569             success : function(req){
9570                //console.log(data);
9571                 var rdata = false;
9572                 var edata;
9573                 try  {
9574                    rdata = Roo.decode(req.responseText)
9575                 } catch (e) {
9576                     Roo.log("Invalid data from server..");
9577                     Roo.log(edata);
9578                     return;
9579                 }
9580                 if (!rdata || !rdata.success) {
9581                     Roo.log(rdata);
9582                     Roo.MessageBox.alert(Roo.encode(rdata));
9583                     return;
9584                 }
9585                 var data = rdata.data;
9586                 
9587                 if (this.uploadComplete) {
9588                    Roo.MessageBox.hide();
9589                    return;
9590                 }
9591                    
9592                 if (data){
9593                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9594                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9595                     );
9596                 }
9597                 this.uploadProgress.defer(2000,this);
9598             },
9599        
9600             failure: function(data) {
9601                 Roo.log('progress url failed ');
9602                 Roo.log(data);
9603             },
9604             scope : this
9605         });
9606            
9607     },
9608     
9609     
9610     run : function()
9611     {
9612         // run get Values on the form, so it syncs any secondary forms.
9613         this.form.getValues();
9614         
9615         var o = this.options;
9616         var method = this.getMethod();
9617         var isPost = method == 'POST';
9618         if(o.clientValidation === false || this.form.isValid()){
9619             
9620             if (this.form.progressUrl) {
9621                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9622                     (new Date() * 1) + '' + Math.random());
9623                     
9624             } 
9625             
9626             
9627             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9628                 form:this.form.el.dom,
9629                 url:this.getUrl(!isPost),
9630                 method: method,
9631                 params:isPost ? this.getParams() : null,
9632                 isUpload: this.form.fileUpload,
9633                 formData : this.form.formData
9634             }));
9635             
9636             this.uploadProgress();
9637
9638         }else if (o.clientValidation !== false){ // client validation failed
9639             this.failureType = Roo.form.Action.CLIENT_INVALID;
9640             this.form.afterAction(this, false);
9641         }
9642     },
9643
9644     success : function(response)
9645     {
9646         this.uploadComplete= true;
9647         if (this.haveProgress) {
9648             Roo.MessageBox.hide();
9649         }
9650         
9651         
9652         var result = this.processResponse(response);
9653         if(result === true || result.success){
9654             this.form.afterAction(this, true);
9655             return;
9656         }
9657         if(result.errors){
9658             this.form.markInvalid(result.errors);
9659             this.failureType = Roo.form.Action.SERVER_INVALID;
9660         }
9661         this.form.afterAction(this, false);
9662     },
9663     failure : function(response)
9664     {
9665         this.uploadComplete= true;
9666         if (this.haveProgress) {
9667             Roo.MessageBox.hide();
9668         }
9669         
9670         this.response = response;
9671         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9672         this.form.afterAction(this, false);
9673     },
9674     
9675     handleResponse : function(response){
9676         if(this.form.errorReader){
9677             var rs = this.form.errorReader.read(response);
9678             var errors = [];
9679             if(rs.records){
9680                 for(var i = 0, len = rs.records.length; i < len; i++) {
9681                     var r = rs.records[i];
9682                     errors[i] = r.data;
9683                 }
9684             }
9685             if(errors.length < 1){
9686                 errors = null;
9687             }
9688             return {
9689                 success : rs.success,
9690                 errors : errors
9691             };
9692         }
9693         var ret = false;
9694         try {
9695             ret = Roo.decode(response.responseText);
9696         } catch (e) {
9697             ret = {
9698                 success: false,
9699                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9700                 errors : []
9701             };
9702         }
9703         return ret;
9704         
9705     }
9706 });
9707
9708
9709 Roo.form.Action.Load = function(form, options){
9710     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9711     this.reader = this.form.reader;
9712 };
9713
9714 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9715     type : 'load',
9716
9717     run : function(){
9718         
9719         Roo.Ajax.request(Roo.apply(
9720                 this.createCallback(), {
9721                     method:this.getMethod(),
9722                     url:this.getUrl(false),
9723                     params:this.getParams()
9724         }));
9725     },
9726
9727     success : function(response){
9728         
9729         var result = this.processResponse(response);
9730         if(result === true || !result.success || !result.data){
9731             this.failureType = Roo.form.Action.LOAD_FAILURE;
9732             this.form.afterAction(this, false);
9733             return;
9734         }
9735         this.form.clearInvalid();
9736         this.form.setValues(result.data);
9737         this.form.afterAction(this, true);
9738     },
9739
9740     handleResponse : function(response){
9741         if(this.form.reader){
9742             var rs = this.form.reader.read(response);
9743             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9744             return {
9745                 success : rs.success,
9746                 data : data
9747             };
9748         }
9749         return Roo.decode(response.responseText);
9750     }
9751 });
9752
9753 Roo.form.Action.ACTION_TYPES = {
9754     'load' : Roo.form.Action.Load,
9755     'submit' : Roo.form.Action.Submit
9756 };/*
9757  * - LGPL
9758  *
9759  * form
9760  *
9761  */
9762
9763 /**
9764  * @class Roo.bootstrap.Form
9765  * @extends Roo.bootstrap.Component
9766  * Bootstrap Form class
9767  * @cfg {String} method  GET | POST (default POST)
9768  * @cfg {String} labelAlign top | left (default top)
9769  * @cfg {String} align left  | right - for navbars
9770  * @cfg {Boolean} loadMask load mask when submit (default true)
9771
9772  *
9773  * @constructor
9774  * Create a new Form
9775  * @param {Object} config The config object
9776  */
9777
9778
9779 Roo.bootstrap.Form = function(config){
9780     
9781     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9782     
9783     Roo.bootstrap.Form.popover.apply();
9784     
9785     this.addEvents({
9786         /**
9787          * @event clientvalidation
9788          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9789          * @param {Form} this
9790          * @param {Boolean} valid true if the form has passed client-side validation
9791          */
9792         clientvalidation: true,
9793         /**
9794          * @event beforeaction
9795          * Fires before any action is performed. Return false to cancel the action.
9796          * @param {Form} this
9797          * @param {Action} action The action to be performed
9798          */
9799         beforeaction: true,
9800         /**
9801          * @event actionfailed
9802          * Fires when an action fails.
9803          * @param {Form} this
9804          * @param {Action} action The action that failed
9805          */
9806         actionfailed : true,
9807         /**
9808          * @event actioncomplete
9809          * Fires when an action is completed.
9810          * @param {Form} this
9811          * @param {Action} action The action that completed
9812          */
9813         actioncomplete : true
9814     });
9815 };
9816
9817 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9818
9819      /**
9820      * @cfg {String} method
9821      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9822      */
9823     method : 'POST',
9824     /**
9825      * @cfg {String} url
9826      * The URL to use for form actions if one isn't supplied in the action options.
9827      */
9828     /**
9829      * @cfg {Boolean} fileUpload
9830      * Set to true if this form is a file upload.
9831      */
9832
9833     /**
9834      * @cfg {Object} baseParams
9835      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9836      */
9837
9838     /**
9839      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9840      */
9841     timeout: 30,
9842     /**
9843      * @cfg {Sting} align (left|right) for navbar forms
9844      */
9845     align : 'left',
9846
9847     // private
9848     activeAction : null,
9849
9850     /**
9851      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9852      * element by passing it or its id or mask the form itself by passing in true.
9853      * @type Mixed
9854      */
9855     waitMsgTarget : false,
9856
9857     loadMask : true,
9858     
9859     /**
9860      * @cfg {Boolean} errorMask (true|false) default false
9861      */
9862     errorMask : false,
9863     
9864     /**
9865      * @cfg {Number} maskOffset Default 100
9866      */
9867     maskOffset : 100,
9868     
9869     /**
9870      * @cfg {Boolean} maskBody
9871      */
9872     maskBody : false,
9873
9874     getAutoCreate : function(){
9875
9876         var cfg = {
9877             tag: 'form',
9878             method : this.method || 'POST',
9879             id : this.id || Roo.id(),
9880             cls : ''
9881         };
9882         if (this.parent().xtype.match(/^Nav/)) {
9883             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9884
9885         }
9886
9887         if (this.labelAlign == 'left' ) {
9888             cfg.cls += ' form-horizontal';
9889         }
9890
9891
9892         return cfg;
9893     },
9894     initEvents : function()
9895     {
9896         this.el.on('submit', this.onSubmit, this);
9897         // this was added as random key presses on the form where triggering form submit.
9898         this.el.on('keypress', function(e) {
9899             if (e.getCharCode() != 13) {
9900                 return true;
9901             }
9902             // we might need to allow it for textareas.. and some other items.
9903             // check e.getTarget().
9904
9905             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9906                 return true;
9907             }
9908
9909             Roo.log("keypress blocked");
9910
9911             e.preventDefault();
9912             return false;
9913         });
9914         
9915     },
9916     // private
9917     onSubmit : function(e){
9918         e.stopEvent();
9919     },
9920
9921      /**
9922      * Returns true if client-side validation on the form is successful.
9923      * @return Boolean
9924      */
9925     isValid : function(){
9926         var items = this.getItems();
9927         var valid = true;
9928         var target = false;
9929         
9930         items.each(function(f){
9931             
9932             if(f.validate()){
9933                 return;
9934             }
9935             
9936             Roo.log('invalid field: ' + f.name);
9937             
9938             valid = false;
9939
9940             if(!target && f.el.isVisible(true)){
9941                 target = f;
9942             }
9943            
9944         });
9945         
9946         if(this.errorMask && !valid){
9947             Roo.bootstrap.Form.popover.mask(this, target);
9948         }
9949         
9950         return valid;
9951     },
9952     
9953     /**
9954      * Returns true if any fields in this form have changed since their original load.
9955      * @return Boolean
9956      */
9957     isDirty : function(){
9958         var dirty = false;
9959         var items = this.getItems();
9960         items.each(function(f){
9961            if(f.isDirty()){
9962                dirty = true;
9963                return false;
9964            }
9965            return true;
9966         });
9967         return dirty;
9968     },
9969      /**
9970      * Performs a predefined action (submit or load) or custom actions you define on this form.
9971      * @param {String} actionName The name of the action type
9972      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9973      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9974      * accept other config options):
9975      * <pre>
9976 Property          Type             Description
9977 ----------------  ---------------  ----------------------------------------------------------------------------------
9978 url               String           The url for the action (defaults to the form's url)
9979 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9980 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9981 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9982                                    validate the form on the client (defaults to false)
9983      * </pre>
9984      * @return {BasicForm} this
9985      */
9986     doAction : function(action, options){
9987         if(typeof action == 'string'){
9988             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9989         }
9990         if(this.fireEvent('beforeaction', this, action) !== false){
9991             this.beforeAction(action);
9992             action.run.defer(100, action);
9993         }
9994         return this;
9995     },
9996
9997     // private
9998     beforeAction : function(action){
9999         var o = action.options;
10000         
10001         if(this.loadMask){
10002             
10003             if(this.maskBody){
10004                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10005             } else {
10006                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10007             }
10008         }
10009         // not really supported yet.. ??
10010
10011         //if(this.waitMsgTarget === true){
10012         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10013         //}else if(this.waitMsgTarget){
10014         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10015         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10016         //}else {
10017         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10018        // }
10019
10020     },
10021
10022     // private
10023     afterAction : function(action, success){
10024         this.activeAction = null;
10025         var o = action.options;
10026
10027         if(this.loadMask){
10028             
10029             if(this.maskBody){
10030                 Roo.get(document.body).unmask();
10031             } else {
10032                 this.el.unmask();
10033             }
10034         }
10035         
10036         //if(this.waitMsgTarget === true){
10037 //            this.el.unmask();
10038         //}else if(this.waitMsgTarget){
10039         //    this.waitMsgTarget.unmask();
10040         //}else{
10041         //    Roo.MessageBox.updateProgress(1);
10042         //    Roo.MessageBox.hide();
10043        // }
10044         //
10045         if(success){
10046             if(o.reset){
10047                 this.reset();
10048             }
10049             Roo.callback(o.success, o.scope, [this, action]);
10050             this.fireEvent('actioncomplete', this, action);
10051
10052         }else{
10053
10054             // failure condition..
10055             // we have a scenario where updates need confirming.
10056             // eg. if a locking scenario exists..
10057             // we look for { errors : { needs_confirm : true }} in the response.
10058             if (
10059                 (typeof(action.result) != 'undefined')  &&
10060                 (typeof(action.result.errors) != 'undefined')  &&
10061                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10062            ){
10063                 var _t = this;
10064                 Roo.log("not supported yet");
10065                  /*
10066
10067                 Roo.MessageBox.confirm(
10068                     "Change requires confirmation",
10069                     action.result.errorMsg,
10070                     function(r) {
10071                         if (r != 'yes') {
10072                             return;
10073                         }
10074                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10075                     }
10076
10077                 );
10078                 */
10079
10080
10081                 return;
10082             }
10083
10084             Roo.callback(o.failure, o.scope, [this, action]);
10085             // show an error message if no failed handler is set..
10086             if (!this.hasListener('actionfailed')) {
10087                 Roo.log("need to add dialog support");
10088                 /*
10089                 Roo.MessageBox.alert("Error",
10090                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10091                         action.result.errorMsg :
10092                         "Saving Failed, please check your entries or try again"
10093                 );
10094                 */
10095             }
10096
10097             this.fireEvent('actionfailed', this, action);
10098         }
10099
10100     },
10101     /**
10102      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10103      * @param {String} id The value to search for
10104      * @return Field
10105      */
10106     findField : function(id){
10107         var items = this.getItems();
10108         var field = items.get(id);
10109         if(!field){
10110              items.each(function(f){
10111                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10112                     field = f;
10113                     return false;
10114                 }
10115                 return true;
10116             });
10117         }
10118         return field || null;
10119     },
10120      /**
10121      * Mark fields in this form invalid in bulk.
10122      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10123      * @return {BasicForm} this
10124      */
10125     markInvalid : function(errors){
10126         if(errors instanceof Array){
10127             for(var i = 0, len = errors.length; i < len; i++){
10128                 var fieldError = errors[i];
10129                 var f = this.findField(fieldError.id);
10130                 if(f){
10131                     f.markInvalid(fieldError.msg);
10132                 }
10133             }
10134         }else{
10135             var field, id;
10136             for(id in errors){
10137                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10138                     field.markInvalid(errors[id]);
10139                 }
10140             }
10141         }
10142         //Roo.each(this.childForms || [], function (f) {
10143         //    f.markInvalid(errors);
10144         //});
10145
10146         return this;
10147     },
10148
10149     /**
10150      * Set values for fields in this form in bulk.
10151      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10152      * @return {BasicForm} this
10153      */
10154     setValues : function(values){
10155         if(values instanceof Array){ // array of objects
10156             for(var i = 0, len = values.length; i < len; i++){
10157                 var v = values[i];
10158                 var f = this.findField(v.id);
10159                 if(f){
10160                     f.setValue(v.value);
10161                     if(this.trackResetOnLoad){
10162                         f.originalValue = f.getValue();
10163                     }
10164                 }
10165             }
10166         }else{ // object hash
10167             var field, id;
10168             for(id in values){
10169                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10170
10171                     if (field.setFromData &&
10172                         field.valueField &&
10173                         field.displayField &&
10174                         // combos' with local stores can
10175                         // be queried via setValue()
10176                         // to set their value..
10177                         (field.store && !field.store.isLocal)
10178                         ) {
10179                         // it's a combo
10180                         var sd = { };
10181                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10182                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10183                         field.setFromData(sd);
10184
10185                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10186                         
10187                         field.setFromData(values);
10188                         
10189                     } else {
10190                         field.setValue(values[id]);
10191                     }
10192
10193
10194                     if(this.trackResetOnLoad){
10195                         field.originalValue = field.getValue();
10196                     }
10197                 }
10198             }
10199         }
10200
10201         //Roo.each(this.childForms || [], function (f) {
10202         //    f.setValues(values);
10203         //});
10204
10205         return this;
10206     },
10207
10208     /**
10209      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10210      * they are returned as an array.
10211      * @param {Boolean} asString
10212      * @return {Object}
10213      */
10214     getValues : function(asString){
10215         //if (this.childForms) {
10216             // copy values from the child forms
10217         //    Roo.each(this.childForms, function (f) {
10218         //        this.setValues(f.getValues());
10219         //    }, this);
10220         //}
10221
10222
10223
10224         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10225         if(asString === true){
10226             return fs;
10227         }
10228         return Roo.urlDecode(fs);
10229     },
10230
10231     /**
10232      * Returns the fields in this form as an object with key/value pairs.
10233      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10234      * @return {Object}
10235      */
10236     getFieldValues : function(with_hidden)
10237     {
10238         var items = this.getItems();
10239         var ret = {};
10240         items.each(function(f){
10241             
10242             if (!f.getName()) {
10243                 return;
10244             }
10245             
10246             var v = f.getValue();
10247             
10248             if (f.inputType =='radio') {
10249                 if (typeof(ret[f.getName()]) == 'undefined') {
10250                     ret[f.getName()] = ''; // empty..
10251                 }
10252
10253                 if (!f.el.dom.checked) {
10254                     return;
10255
10256                 }
10257                 v = f.el.dom.value;
10258
10259             }
10260             
10261             if(f.xtype == 'MoneyField'){
10262                 ret[f.currencyName] = f.getCurrency();
10263             }
10264
10265             // not sure if this supported any more..
10266             if ((typeof(v) == 'object') && f.getRawValue) {
10267                 v = f.getRawValue() ; // dates..
10268             }
10269             // combo boxes where name != hiddenName...
10270             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10271                 ret[f.name] = f.getRawValue();
10272             }
10273             ret[f.getName()] = v;
10274         });
10275
10276         return ret;
10277     },
10278
10279     /**
10280      * Clears all invalid messages in this form.
10281      * @return {BasicForm} this
10282      */
10283     clearInvalid : function(){
10284         var items = this.getItems();
10285
10286         items.each(function(f){
10287            f.clearInvalid();
10288         });
10289
10290         return this;
10291     },
10292
10293     /**
10294      * Resets this form.
10295      * @return {BasicForm} this
10296      */
10297     reset : function(){
10298         var items = this.getItems();
10299         items.each(function(f){
10300             f.reset();
10301         });
10302
10303         Roo.each(this.childForms || [], function (f) {
10304             f.reset();
10305         });
10306
10307
10308         return this;
10309     },
10310     
10311     getItems : function()
10312     {
10313         var r=new Roo.util.MixedCollection(false, function(o){
10314             return o.id || (o.id = Roo.id());
10315         });
10316         var iter = function(el) {
10317             if (el.inputEl) {
10318                 r.add(el);
10319             }
10320             if (!el.items) {
10321                 return;
10322             }
10323             Roo.each(el.items,function(e) {
10324                 iter(e);
10325             });
10326         };
10327
10328         iter(this);
10329         return r;
10330     },
10331     
10332     hideFields : function(items)
10333     {
10334         Roo.each(items, function(i){
10335             
10336             var f = this.findField(i);
10337             
10338             if(!f){
10339                 return;
10340             }
10341             
10342             f.hide();
10343             
10344         }, this);
10345     },
10346     
10347     showFields : function(items)
10348     {
10349         Roo.each(items, function(i){
10350             
10351             var f = this.findField(i);
10352             
10353             if(!f){
10354                 return;
10355             }
10356             
10357             f.show();
10358             
10359         }, this);
10360     }
10361
10362 });
10363
10364 Roo.apply(Roo.bootstrap.Form, {
10365     
10366     popover : {
10367         
10368         padding : 5,
10369         
10370         isApplied : false,
10371         
10372         isMasked : false,
10373         
10374         form : false,
10375         
10376         target : false,
10377         
10378         toolTip : false,
10379         
10380         intervalID : false,
10381         
10382         maskEl : false,
10383         
10384         apply : function()
10385         {
10386             if(this.isApplied){
10387                 return;
10388             }
10389             
10390             this.maskEl = {
10391                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10392                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10393                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10394                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10395             };
10396             
10397             this.maskEl.top.enableDisplayMode("block");
10398             this.maskEl.left.enableDisplayMode("block");
10399             this.maskEl.bottom.enableDisplayMode("block");
10400             this.maskEl.right.enableDisplayMode("block");
10401             
10402             this.toolTip = new Roo.bootstrap.Tooltip({
10403                 cls : 'roo-form-error-popover',
10404                 alignment : {
10405                     'left' : ['r-l', [-2,0], 'right'],
10406                     'right' : ['l-r', [2,0], 'left'],
10407                     'bottom' : ['tl-bl', [0,2], 'top'],
10408                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10409                 }
10410             });
10411             
10412             this.toolTip.render(Roo.get(document.body));
10413
10414             this.toolTip.el.enableDisplayMode("block");
10415             
10416             Roo.get(document.body).on('click', function(){
10417                 this.unmask();
10418             }, this);
10419             
10420             Roo.get(document.body).on('touchstart', function(){
10421                 this.unmask();
10422             }, this);
10423             
10424             this.isApplied = true
10425         },
10426         
10427         mask : function(form, target)
10428         {
10429             this.form = form;
10430             
10431             this.target = target;
10432             
10433             if(!this.form.errorMask || !target.el){
10434                 return;
10435             }
10436             
10437             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10438             
10439             Roo.log(scrollable);
10440             
10441             var ot = this.target.el.calcOffsetsTo(scrollable);
10442             
10443             var scrollTo = ot[1] - this.form.maskOffset;
10444             
10445             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10446             
10447             scrollable.scrollTo('top', scrollTo);
10448             
10449             var box = this.target.el.getBox();
10450             Roo.log(box);
10451             var zIndex = Roo.bootstrap.Modal.zIndex++;
10452
10453             
10454             this.maskEl.top.setStyle('position', 'absolute');
10455             this.maskEl.top.setStyle('z-index', zIndex);
10456             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10457             this.maskEl.top.setLeft(0);
10458             this.maskEl.top.setTop(0);
10459             this.maskEl.top.show();
10460             
10461             this.maskEl.left.setStyle('position', 'absolute');
10462             this.maskEl.left.setStyle('z-index', zIndex);
10463             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10464             this.maskEl.left.setLeft(0);
10465             this.maskEl.left.setTop(box.y - this.padding);
10466             this.maskEl.left.show();
10467
10468             this.maskEl.bottom.setStyle('position', 'absolute');
10469             this.maskEl.bottom.setStyle('z-index', zIndex);
10470             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10471             this.maskEl.bottom.setLeft(0);
10472             this.maskEl.bottom.setTop(box.bottom + this.padding);
10473             this.maskEl.bottom.show();
10474
10475             this.maskEl.right.setStyle('position', 'absolute');
10476             this.maskEl.right.setStyle('z-index', zIndex);
10477             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10478             this.maskEl.right.setLeft(box.right + this.padding);
10479             this.maskEl.right.setTop(box.y - this.padding);
10480             this.maskEl.right.show();
10481
10482             this.toolTip.bindEl = this.target.el;
10483
10484             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10485
10486             var tip = this.target.blankText;
10487
10488             if(this.target.getValue() !== '' ) {
10489                 
10490                 if (this.target.invalidText.length) {
10491                     tip = this.target.invalidText;
10492                 } else if (this.target.regexText.length){
10493                     tip = this.target.regexText;
10494                 }
10495             }
10496
10497             this.toolTip.show(tip);
10498
10499             this.intervalID = window.setInterval(function() {
10500                 Roo.bootstrap.Form.popover.unmask();
10501             }, 10000);
10502
10503             window.onwheel = function(){ return false;};
10504             
10505             (function(){ this.isMasked = true; }).defer(500, this);
10506             
10507         },
10508         
10509         unmask : function()
10510         {
10511             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10512                 return;
10513             }
10514             
10515             this.maskEl.top.setStyle('position', 'absolute');
10516             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10517             this.maskEl.top.hide();
10518
10519             this.maskEl.left.setStyle('position', 'absolute');
10520             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10521             this.maskEl.left.hide();
10522
10523             this.maskEl.bottom.setStyle('position', 'absolute');
10524             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10525             this.maskEl.bottom.hide();
10526
10527             this.maskEl.right.setStyle('position', 'absolute');
10528             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10529             this.maskEl.right.hide();
10530             
10531             this.toolTip.hide();
10532             
10533             this.toolTip.el.hide();
10534             
10535             window.onwheel = function(){ return true;};
10536             
10537             if(this.intervalID){
10538                 window.clearInterval(this.intervalID);
10539                 this.intervalID = false;
10540             }
10541             
10542             this.isMasked = false;
10543             
10544         }
10545         
10546     }
10547     
10548 });
10549
10550 /*
10551  * Based on:
10552  * Ext JS Library 1.1.1
10553  * Copyright(c) 2006-2007, Ext JS, LLC.
10554  *
10555  * Originally Released Under LGPL - original licence link has changed is not relivant.
10556  *
10557  * Fork - LGPL
10558  * <script type="text/javascript">
10559  */
10560 /**
10561  * @class Roo.form.VTypes
10562  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10563  * @singleton
10564  */
10565 Roo.form.VTypes = function(){
10566     // closure these in so they are only created once.
10567     var alpha = /^[a-zA-Z_]+$/;
10568     var alphanum = /^[a-zA-Z0-9_]+$/;
10569     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10570     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10571
10572     // All these messages and functions are configurable
10573     return {
10574         /**
10575          * The function used to validate email addresses
10576          * @param {String} value The email address
10577          */
10578         'email' : function(v){
10579             return email.test(v);
10580         },
10581         /**
10582          * The error text to display when the email validation function returns false
10583          * @type String
10584          */
10585         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10586         /**
10587          * The keystroke filter mask to be applied on email input
10588          * @type RegExp
10589          */
10590         'emailMask' : /[a-z0-9_\.\-@]/i,
10591
10592         /**
10593          * The function used to validate URLs
10594          * @param {String} value The URL
10595          */
10596         'url' : function(v){
10597             return url.test(v);
10598         },
10599         /**
10600          * The error text to display when the url validation function returns false
10601          * @type String
10602          */
10603         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10604         
10605         /**
10606          * The function used to validate alpha values
10607          * @param {String} value The value
10608          */
10609         'alpha' : function(v){
10610             return alpha.test(v);
10611         },
10612         /**
10613          * The error text to display when the alpha validation function returns false
10614          * @type String
10615          */
10616         'alphaText' : 'This field should only contain letters and _',
10617         /**
10618          * The keystroke filter mask to be applied on alpha input
10619          * @type RegExp
10620          */
10621         'alphaMask' : /[a-z_]/i,
10622
10623         /**
10624          * The function used to validate alphanumeric values
10625          * @param {String} value The value
10626          */
10627         'alphanum' : function(v){
10628             return alphanum.test(v);
10629         },
10630         /**
10631          * The error text to display when the alphanumeric validation function returns false
10632          * @type String
10633          */
10634         'alphanumText' : 'This field should only contain letters, numbers and _',
10635         /**
10636          * The keystroke filter mask to be applied on alphanumeric input
10637          * @type RegExp
10638          */
10639         'alphanumMask' : /[a-z0-9_]/i
10640     };
10641 }();/*
10642  * - LGPL
10643  *
10644  * Input
10645  * 
10646  */
10647
10648 /**
10649  * @class Roo.bootstrap.Input
10650  * @extends Roo.bootstrap.Component
10651  * Bootstrap Input class
10652  * @cfg {Boolean} disabled is it disabled
10653  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10654  * @cfg {String} name name of the input
10655  * @cfg {string} fieldLabel - the label associated
10656  * @cfg {string} placeholder - placeholder to put in text.
10657  * @cfg {string}  before - input group add on before
10658  * @cfg {string} after - input group add on after
10659  * @cfg {string} size - (lg|sm) or leave empty..
10660  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10661  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10662  * @cfg {Number} md colspan out of 12 for computer-sized screens
10663  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10664  * @cfg {string} value default value of the input
10665  * @cfg {Number} labelWidth set the width of label 
10666  * @cfg {Number} labellg set the width of label (1-12)
10667  * @cfg {Number} labelmd set the width of label (1-12)
10668  * @cfg {Number} labelsm set the width of label (1-12)
10669  * @cfg {Number} labelxs set the width of label (1-12)
10670  * @cfg {String} labelAlign (top|left)
10671  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10672  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10673  * @cfg {String} indicatorpos (left|right) default left
10674  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10675  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10676  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10677
10678  * @cfg {String} align (left|center|right) Default left
10679  * @cfg {Boolean} forceFeedback (true|false) Default false
10680  * 
10681  * @constructor
10682  * Create a new Input
10683  * @param {Object} config The config object
10684  */
10685
10686 Roo.bootstrap.Input = function(config){
10687     
10688     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10689     
10690     this.addEvents({
10691         /**
10692          * @event focus
10693          * Fires when this field receives input focus.
10694          * @param {Roo.form.Field} this
10695          */
10696         focus : true,
10697         /**
10698          * @event blur
10699          * Fires when this field loses input focus.
10700          * @param {Roo.form.Field} this
10701          */
10702         blur : true,
10703         /**
10704          * @event specialkey
10705          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10706          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10707          * @param {Roo.form.Field} this
10708          * @param {Roo.EventObject} e The event object
10709          */
10710         specialkey : true,
10711         /**
10712          * @event change
10713          * Fires just before the field blurs if the field value has changed.
10714          * @param {Roo.form.Field} this
10715          * @param {Mixed} newValue The new value
10716          * @param {Mixed} oldValue The original value
10717          */
10718         change : true,
10719         /**
10720          * @event invalid
10721          * Fires after the field has been marked as invalid.
10722          * @param {Roo.form.Field} this
10723          * @param {String} msg The validation message
10724          */
10725         invalid : true,
10726         /**
10727          * @event valid
10728          * Fires after the field has been validated with no errors.
10729          * @param {Roo.form.Field} this
10730          */
10731         valid : true,
10732          /**
10733          * @event keyup
10734          * Fires after the key up
10735          * @param {Roo.form.Field} this
10736          * @param {Roo.EventObject}  e The event Object
10737          */
10738         keyup : true,
10739         /**
10740          * @event paste
10741          * Fires after the user pastes into input
10742          * @param {Roo.form.Field} this
10743          * @param {Roo.EventObject}  e The event Object
10744          */
10745         paste : true
10746     });
10747 };
10748
10749 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10750      /**
10751      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10752       automatic validation (defaults to "keyup").
10753      */
10754     validationEvent : "keyup",
10755      /**
10756      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10757      */
10758     validateOnBlur : true,
10759     /**
10760      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10761      */
10762     validationDelay : 250,
10763      /**
10764      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10765      */
10766     focusClass : "x-form-focus",  // not needed???
10767     
10768        
10769     /**
10770      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10771      */
10772     invalidClass : "has-warning",
10773     
10774     /**
10775      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10776      */
10777     validClass : "has-success",
10778     
10779     /**
10780      * @cfg {Boolean} hasFeedback (true|false) default true
10781      */
10782     hasFeedback : true,
10783     
10784     /**
10785      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10786      */
10787     invalidFeedbackClass : "glyphicon-warning-sign",
10788     
10789     /**
10790      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10791      */
10792     validFeedbackClass : "glyphicon-ok",
10793     
10794     /**
10795      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10796      */
10797     selectOnFocus : false,
10798     
10799      /**
10800      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10801      */
10802     maskRe : null,
10803        /**
10804      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10805      */
10806     vtype : null,
10807     
10808       /**
10809      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10810      */
10811     disableKeyFilter : false,
10812     
10813        /**
10814      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10815      */
10816     disabled : false,
10817      /**
10818      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10819      */
10820     allowBlank : true,
10821     /**
10822      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10823      */
10824     blankText : "Please complete this mandatory field",
10825     
10826      /**
10827      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10828      */
10829     minLength : 0,
10830     /**
10831      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10832      */
10833     maxLength : Number.MAX_VALUE,
10834     /**
10835      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10836      */
10837     minLengthText : "The minimum length for this field is {0}",
10838     /**
10839      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10840      */
10841     maxLengthText : "The maximum length for this field is {0}",
10842   
10843     
10844     /**
10845      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10846      * If available, this function will be called only after the basic validators all return true, and will be passed the
10847      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10848      */
10849     validator : null,
10850     /**
10851      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10852      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10853      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10854      */
10855     regex : null,
10856     /**
10857      * @cfg {String} regexText -- Depricated - use Invalid Text
10858      */
10859     regexText : "",
10860     
10861     /**
10862      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10863      */
10864     invalidText : "",
10865     
10866     
10867     
10868     autocomplete: false,
10869     
10870     
10871     fieldLabel : '',
10872     inputType : 'text',
10873     
10874     name : false,
10875     placeholder: false,
10876     before : false,
10877     after : false,
10878     size : false,
10879     hasFocus : false,
10880     preventMark: false,
10881     isFormField : true,
10882     value : '',
10883     labelWidth : 2,
10884     labelAlign : false,
10885     readOnly : false,
10886     align : false,
10887     formatedValue : false,
10888     forceFeedback : false,
10889     
10890     indicatorpos : 'left',
10891     
10892     labellg : 0,
10893     labelmd : 0,
10894     labelsm : 0,
10895     labelxs : 0,
10896     
10897     capture : '',
10898     accept : '',
10899     
10900     parentLabelAlign : function()
10901     {
10902         var parent = this;
10903         while (parent.parent()) {
10904             parent = parent.parent();
10905             if (typeof(parent.labelAlign) !='undefined') {
10906                 return parent.labelAlign;
10907             }
10908         }
10909         return 'left';
10910         
10911     },
10912     
10913     getAutoCreate : function()
10914     {
10915         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10916         
10917         var id = Roo.id();
10918         
10919         var cfg = {};
10920         
10921         if(this.inputType != 'hidden'){
10922             cfg.cls = 'form-group' //input-group
10923         }
10924         
10925         var input =  {
10926             tag: 'input',
10927             id : id,
10928             type : this.inputType,
10929             value : this.value,
10930             cls : 'form-control',
10931             placeholder : this.placeholder || '',
10932             autocomplete : this.autocomplete || 'new-password'
10933         };
10934         if (this.inputType == 'file') {
10935             input.style = 'overflow:hidden'; // why not in CSS?
10936         }
10937         
10938         if(this.capture.length){
10939             input.capture = this.capture;
10940         }
10941         
10942         if(this.accept.length){
10943             input.accept = this.accept + "/*";
10944         }
10945         
10946         if(this.align){
10947             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10948         }
10949         
10950         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10951             input.maxLength = this.maxLength;
10952         }
10953         
10954         if (this.disabled) {
10955             input.disabled=true;
10956         }
10957         
10958         if (this.readOnly) {
10959             input.readonly=true;
10960         }
10961         
10962         if (this.name) {
10963             input.name = this.name;
10964         }
10965         
10966         if (this.size) {
10967             input.cls += ' input-' + this.size;
10968         }
10969         
10970         var settings=this;
10971         ['xs','sm','md','lg'].map(function(size){
10972             if (settings[size]) {
10973                 cfg.cls += ' col-' + size + '-' + settings[size];
10974             }
10975         });
10976         
10977         var inputblock = input;
10978         
10979         var feedback = {
10980             tag: 'span',
10981             cls: 'glyphicon form-control-feedback'
10982         };
10983             
10984         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10985             
10986             inputblock = {
10987                 cls : 'has-feedback',
10988                 cn :  [
10989                     input,
10990                     feedback
10991                 ] 
10992             };  
10993         }
10994         
10995         if (this.before || this.after) {
10996             
10997             inputblock = {
10998                 cls : 'input-group',
10999                 cn :  [] 
11000             };
11001             
11002             if (this.before && typeof(this.before) == 'string') {
11003                 
11004                 inputblock.cn.push({
11005                     tag :'span',
11006                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11007                     html : this.before
11008                 });
11009             }
11010             if (this.before && typeof(this.before) == 'object') {
11011                 this.before = Roo.factory(this.before);
11012                 
11013                 inputblock.cn.push({
11014                     tag :'span',
11015                     cls : 'roo-input-before input-group-prepend   input-group-' +
11016                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11017                 });
11018             }
11019             
11020             inputblock.cn.push(input);
11021             
11022             if (this.after && typeof(this.after) == 'string') {
11023                 inputblock.cn.push({
11024                     tag :'span',
11025                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11026                     html : this.after
11027                 });
11028             }
11029             if (this.after && typeof(this.after) == 'object') {
11030                 this.after = Roo.factory(this.after);
11031                 
11032                 inputblock.cn.push({
11033                     tag :'span',
11034                     cls : 'roo-input-after input-group-append  input-group-' +
11035                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11036                 });
11037             }
11038             
11039             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11040                 inputblock.cls += ' has-feedback';
11041                 inputblock.cn.push(feedback);
11042             }
11043         };
11044         var indicator = {
11045             tag : 'i',
11046             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11047             tooltip : 'This field is required'
11048         };
11049         if (this.allowBlank ) {
11050             indicator.style = this.allowBlank ? ' display:none' : '';
11051         }
11052         if (align ==='left' && this.fieldLabel.length) {
11053             
11054             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11055             
11056             cfg.cn = [
11057                 indicator,
11058                 {
11059                     tag: 'label',
11060                     'for' :  id,
11061                     cls : 'control-label col-form-label',
11062                     html : this.fieldLabel
11063
11064                 },
11065                 {
11066                     cls : "", 
11067                     cn: [
11068                         inputblock
11069                     ]
11070                 }
11071             ];
11072             
11073             var labelCfg = cfg.cn[1];
11074             var contentCfg = cfg.cn[2];
11075             
11076             if(this.indicatorpos == 'right'){
11077                 cfg.cn = [
11078                     {
11079                         tag: 'label',
11080                         'for' :  id,
11081                         cls : 'control-label col-form-label',
11082                         cn : [
11083                             {
11084                                 tag : 'span',
11085                                 html : this.fieldLabel
11086                             },
11087                             indicator
11088                         ]
11089                     },
11090                     {
11091                         cls : "",
11092                         cn: [
11093                             inputblock
11094                         ]
11095                     }
11096
11097                 ];
11098                 
11099                 labelCfg = cfg.cn[0];
11100                 contentCfg = cfg.cn[1];
11101             
11102             }
11103             
11104             if(this.labelWidth > 12){
11105                 labelCfg.style = "width: " + this.labelWidth + 'px';
11106             }
11107             
11108             if(this.labelWidth < 13 && this.labelmd == 0){
11109                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11110             }
11111             
11112             if(this.labellg > 0){
11113                 labelCfg.cls += ' col-lg-' + this.labellg;
11114                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11115             }
11116             
11117             if(this.labelmd > 0){
11118                 labelCfg.cls += ' col-md-' + this.labelmd;
11119                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11120             }
11121             
11122             if(this.labelsm > 0){
11123                 labelCfg.cls += ' col-sm-' + this.labelsm;
11124                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11125             }
11126             
11127             if(this.labelxs > 0){
11128                 labelCfg.cls += ' col-xs-' + this.labelxs;
11129                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11130             }
11131             
11132             
11133         } else if ( this.fieldLabel.length) {
11134                 
11135             
11136             
11137             cfg.cn = [
11138                 {
11139                     tag : 'i',
11140                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11141                     tooltip : 'This field is required',
11142                     style : this.allowBlank ? ' display:none' : '' 
11143                 },
11144                 {
11145                     tag: 'label',
11146                    //cls : 'input-group-addon',
11147                     html : this.fieldLabel
11148
11149                 },
11150
11151                inputblock
11152
11153            ];
11154            
11155            if(this.indicatorpos == 'right'){
11156        
11157                 cfg.cn = [
11158                     {
11159                         tag: 'label',
11160                        //cls : 'input-group-addon',
11161                         html : this.fieldLabel
11162
11163                     },
11164                     {
11165                         tag : 'i',
11166                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11167                         tooltip : 'This field is required',
11168                         style : this.allowBlank ? ' display:none' : '' 
11169                     },
11170
11171                    inputblock
11172
11173                ];
11174
11175             }
11176
11177         } else {
11178             
11179             cfg.cn = [
11180
11181                     inputblock
11182
11183             ];
11184                 
11185                 
11186         };
11187         
11188         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11189            cfg.cls += ' navbar-form';
11190         }
11191         
11192         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11193             // on BS4 we do this only if not form 
11194             cfg.cls += ' navbar-form';
11195             cfg.tag = 'li';
11196         }
11197         
11198         return cfg;
11199         
11200     },
11201     /**
11202      * return the real input element.
11203      */
11204     inputEl: function ()
11205     {
11206         return this.el.select('input.form-control',true).first();
11207     },
11208     
11209     tooltipEl : function()
11210     {
11211         return this.inputEl();
11212     },
11213     
11214     indicatorEl : function()
11215     {
11216         if (Roo.bootstrap.version == 4) {
11217             return false; // not enabled in v4 yet.
11218         }
11219         
11220         var indicator = this.el.select('i.roo-required-indicator',true).first();
11221         
11222         if(!indicator){
11223             return false;
11224         }
11225         
11226         return indicator;
11227         
11228     },
11229     
11230     setDisabled : function(v)
11231     {
11232         var i  = this.inputEl().dom;
11233         if (!v) {
11234             i.removeAttribute('disabled');
11235             return;
11236             
11237         }
11238         i.setAttribute('disabled','true');
11239     },
11240     initEvents : function()
11241     {
11242           
11243         this.inputEl().on("keydown" , this.fireKey,  this);
11244         this.inputEl().on("focus", this.onFocus,  this);
11245         this.inputEl().on("blur", this.onBlur,  this);
11246         
11247         this.inputEl().relayEvent('keyup', this);
11248         this.inputEl().relayEvent('paste', this);
11249         
11250         this.indicator = this.indicatorEl();
11251         
11252         if(this.indicator){
11253             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11254         }
11255  
11256         // reference to original value for reset
11257         this.originalValue = this.getValue();
11258         //Roo.form.TextField.superclass.initEvents.call(this);
11259         if(this.validationEvent == 'keyup'){
11260             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11261             this.inputEl().on('keyup', this.filterValidation, this);
11262         }
11263         else if(this.validationEvent !== false){
11264             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11265         }
11266         
11267         if(this.selectOnFocus){
11268             this.on("focus", this.preFocus, this);
11269             
11270         }
11271         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11272             this.inputEl().on("keypress", this.filterKeys, this);
11273         } else {
11274             this.inputEl().relayEvent('keypress', this);
11275         }
11276        /* if(this.grow){
11277             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11278             this.el.on("click", this.autoSize,  this);
11279         }
11280         */
11281         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11282             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11283         }
11284         
11285         if (typeof(this.before) == 'object') {
11286             this.before.render(this.el.select('.roo-input-before',true).first());
11287         }
11288         if (typeof(this.after) == 'object') {
11289             this.after.render(this.el.select('.roo-input-after',true).first());
11290         }
11291         
11292         this.inputEl().on('change', this.onChange, this);
11293         
11294     },
11295     filterValidation : function(e){
11296         if(!e.isNavKeyPress()){
11297             this.validationTask.delay(this.validationDelay);
11298         }
11299     },
11300      /**
11301      * Validates the field value
11302      * @return {Boolean} True if the value is valid, else false
11303      */
11304     validate : function(){
11305         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11306         if(this.disabled || this.validateValue(this.getRawValue())){
11307             this.markValid();
11308             return true;
11309         }
11310         
11311         this.markInvalid();
11312         return false;
11313     },
11314     
11315     
11316     /**
11317      * Validates a value according to the field's validation rules and marks the field as invalid
11318      * if the validation fails
11319      * @param {Mixed} value The value to validate
11320      * @return {Boolean} True if the value is valid, else false
11321      */
11322     validateValue : function(value)
11323     {
11324         if(this.getVisibilityEl().hasClass('hidden')){
11325             return true;
11326         }
11327         
11328         if(value.length < 1)  { // if it's blank
11329             if(this.allowBlank){
11330                 return true;
11331             }
11332             return false;
11333         }
11334         
11335         if(value.length < this.minLength){
11336             return false;
11337         }
11338         if(value.length > this.maxLength){
11339             return false;
11340         }
11341         if(this.vtype){
11342             var vt = Roo.form.VTypes;
11343             if(!vt[this.vtype](value, this)){
11344                 return false;
11345             }
11346         }
11347         if(typeof this.validator == "function"){
11348             var msg = this.validator(value);
11349             if(msg !== true){
11350                 return false;
11351             }
11352             if (typeof(msg) == 'string') {
11353                 this.invalidText = msg;
11354             }
11355         }
11356         
11357         if(this.regex && !this.regex.test(value)){
11358             return false;
11359         }
11360         
11361         return true;
11362     },
11363     
11364      // private
11365     fireKey : function(e){
11366         //Roo.log('field ' + e.getKey());
11367         if(e.isNavKeyPress()){
11368             this.fireEvent("specialkey", this, e);
11369         }
11370     },
11371     focus : function (selectText){
11372         if(this.rendered){
11373             this.inputEl().focus();
11374             if(selectText === true){
11375                 this.inputEl().dom.select();
11376             }
11377         }
11378         return this;
11379     } ,
11380     
11381     onFocus : function(){
11382         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11383            // this.el.addClass(this.focusClass);
11384         }
11385         if(!this.hasFocus){
11386             this.hasFocus = true;
11387             this.startValue = this.getValue();
11388             this.fireEvent("focus", this);
11389         }
11390     },
11391     
11392     beforeBlur : Roo.emptyFn,
11393
11394     
11395     // private
11396     onBlur : function(){
11397         this.beforeBlur();
11398         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11399             //this.el.removeClass(this.focusClass);
11400         }
11401         this.hasFocus = false;
11402         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11403             this.validate();
11404         }
11405         var v = this.getValue();
11406         if(String(v) !== String(this.startValue)){
11407             this.fireEvent('change', this, v, this.startValue);
11408         }
11409         this.fireEvent("blur", this);
11410     },
11411     
11412     onChange : function(e)
11413     {
11414         var v = this.getValue();
11415         if(String(v) !== String(this.startValue)){
11416             this.fireEvent('change', this, v, this.startValue);
11417         }
11418         
11419     },
11420     
11421     /**
11422      * Resets the current field value to the originally loaded value and clears any validation messages
11423      */
11424     reset : function(){
11425         this.setValue(this.originalValue);
11426         this.validate();
11427     },
11428      /**
11429      * Returns the name of the field
11430      * @return {Mixed} name The name field
11431      */
11432     getName: function(){
11433         return this.name;
11434     },
11435      /**
11436      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11437      * @return {Mixed} value The field value
11438      */
11439     getValue : function(){
11440         
11441         var v = this.inputEl().getValue();
11442         
11443         return v;
11444     },
11445     /**
11446      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11447      * @return {Mixed} value The field value
11448      */
11449     getRawValue : function(){
11450         var v = this.inputEl().getValue();
11451         
11452         return v;
11453     },
11454     
11455     /**
11456      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11457      * @param {Mixed} value The value to set
11458      */
11459     setRawValue : function(v){
11460         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11461     },
11462     
11463     selectText : function(start, end){
11464         var v = this.getRawValue();
11465         if(v.length > 0){
11466             start = start === undefined ? 0 : start;
11467             end = end === undefined ? v.length : end;
11468             var d = this.inputEl().dom;
11469             if(d.setSelectionRange){
11470                 d.setSelectionRange(start, end);
11471             }else if(d.createTextRange){
11472                 var range = d.createTextRange();
11473                 range.moveStart("character", start);
11474                 range.moveEnd("character", v.length-end);
11475                 range.select();
11476             }
11477         }
11478     },
11479     
11480     /**
11481      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11482      * @param {Mixed} value The value to set
11483      */
11484     setValue : function(v){
11485         this.value = v;
11486         if(this.rendered){
11487             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11488             this.validate();
11489         }
11490     },
11491     
11492     /*
11493     processValue : function(value){
11494         if(this.stripCharsRe){
11495             var newValue = value.replace(this.stripCharsRe, '');
11496             if(newValue !== value){
11497                 this.setRawValue(newValue);
11498                 return newValue;
11499             }
11500         }
11501         return value;
11502     },
11503   */
11504     preFocus : function(){
11505         
11506         if(this.selectOnFocus){
11507             this.inputEl().dom.select();
11508         }
11509     },
11510     filterKeys : function(e){
11511         var k = e.getKey();
11512         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11513             return;
11514         }
11515         var c = e.getCharCode(), cc = String.fromCharCode(c);
11516         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11517             return;
11518         }
11519         if(!this.maskRe.test(cc)){
11520             e.stopEvent();
11521         }
11522     },
11523      /**
11524      * Clear any invalid styles/messages for this field
11525      */
11526     clearInvalid : function(){
11527         
11528         if(!this.el || this.preventMark){ // not rendered
11529             return;
11530         }
11531         
11532         
11533         this.el.removeClass([this.invalidClass, 'is-invalid']);
11534         
11535         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11536             
11537             var feedback = this.el.select('.form-control-feedback', true).first();
11538             
11539             if(feedback){
11540                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11541             }
11542             
11543         }
11544         
11545         if(this.indicator){
11546             this.indicator.removeClass('visible');
11547             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11548         }
11549         
11550         this.fireEvent('valid', this);
11551     },
11552     
11553      /**
11554      * Mark this field as valid
11555      */
11556     markValid : function()
11557     {
11558         if(!this.el  || this.preventMark){ // not rendered...
11559             return;
11560         }
11561         
11562         this.el.removeClass([this.invalidClass, this.validClass]);
11563         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11564
11565         var feedback = this.el.select('.form-control-feedback', true).first();
11566             
11567         if(feedback){
11568             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11569         }
11570         
11571         if(this.indicator){
11572             this.indicator.removeClass('visible');
11573             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11574         }
11575         
11576         if(this.disabled){
11577             return;
11578         }
11579         
11580            
11581         if(this.allowBlank && !this.getRawValue().length){
11582             return;
11583         }
11584         if (Roo.bootstrap.version == 3) {
11585             this.el.addClass(this.validClass);
11586         } else {
11587             this.inputEl().addClass('is-valid');
11588         }
11589
11590         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11591             
11592             var feedback = this.el.select('.form-control-feedback', true).first();
11593             
11594             if(feedback){
11595                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11596                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11597             }
11598             
11599         }
11600         
11601         this.fireEvent('valid', this);
11602     },
11603     
11604      /**
11605      * Mark this field as invalid
11606      * @param {String} msg The validation message
11607      */
11608     markInvalid : function(msg)
11609     {
11610         if(!this.el  || this.preventMark){ // not rendered
11611             return;
11612         }
11613         
11614         this.el.removeClass([this.invalidClass, this.validClass]);
11615         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11616         
11617         var feedback = this.el.select('.form-control-feedback', true).first();
11618             
11619         if(feedback){
11620             this.el.select('.form-control-feedback', true).first().removeClass(
11621                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11622         }
11623
11624         if(this.disabled){
11625             return;
11626         }
11627         
11628         if(this.allowBlank && !this.getRawValue().length){
11629             return;
11630         }
11631         
11632         if(this.indicator){
11633             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11634             this.indicator.addClass('visible');
11635         }
11636         if (Roo.bootstrap.version == 3) {
11637             this.el.addClass(this.invalidClass);
11638         } else {
11639             this.inputEl().addClass('is-invalid');
11640         }
11641         
11642         
11643         
11644         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11645             
11646             var feedback = this.el.select('.form-control-feedback', true).first();
11647             
11648             if(feedback){
11649                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11650                 
11651                 if(this.getValue().length || this.forceFeedback){
11652                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11653                 }
11654                 
11655             }
11656             
11657         }
11658         
11659         this.fireEvent('invalid', this, msg);
11660     },
11661     // private
11662     SafariOnKeyDown : function(event)
11663     {
11664         // this is a workaround for a password hang bug on chrome/ webkit.
11665         if (this.inputEl().dom.type != 'password') {
11666             return;
11667         }
11668         
11669         var isSelectAll = false;
11670         
11671         if(this.inputEl().dom.selectionEnd > 0){
11672             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11673         }
11674         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11675             event.preventDefault();
11676             this.setValue('');
11677             return;
11678         }
11679         
11680         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11681             
11682             event.preventDefault();
11683             // this is very hacky as keydown always get's upper case.
11684             //
11685             var cc = String.fromCharCode(event.getCharCode());
11686             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11687             
11688         }
11689     },
11690     adjustWidth : function(tag, w){
11691         tag = tag.toLowerCase();
11692         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11693             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11694                 if(tag == 'input'){
11695                     return w + 2;
11696                 }
11697                 if(tag == 'textarea'){
11698                     return w-2;
11699                 }
11700             }else if(Roo.isOpera){
11701                 if(tag == 'input'){
11702                     return w + 2;
11703                 }
11704                 if(tag == 'textarea'){
11705                     return w-2;
11706                 }
11707             }
11708         }
11709         return w;
11710     },
11711     
11712     setFieldLabel : function(v)
11713     {
11714         if(!this.rendered){
11715             return;
11716         }
11717         
11718         if(this.indicatorEl()){
11719             var ar = this.el.select('label > span',true);
11720             
11721             if (ar.elements.length) {
11722                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11723                 this.fieldLabel = v;
11724                 return;
11725             }
11726             
11727             var br = this.el.select('label',true);
11728             
11729             if(br.elements.length) {
11730                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11731                 this.fieldLabel = v;
11732                 return;
11733             }
11734             
11735             Roo.log('Cannot Found any of label > span || label in input');
11736             return;
11737         }
11738         
11739         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11740         this.fieldLabel = v;
11741         
11742         
11743     }
11744 });
11745
11746  
11747 /*
11748  * - LGPL
11749  *
11750  * Input
11751  * 
11752  */
11753
11754 /**
11755  * @class Roo.bootstrap.TextArea
11756  * @extends Roo.bootstrap.Input
11757  * Bootstrap TextArea class
11758  * @cfg {Number} cols Specifies the visible width of a text area
11759  * @cfg {Number} rows Specifies the visible number of lines in a text area
11760  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11761  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11762  * @cfg {string} html text
11763  * 
11764  * @constructor
11765  * Create a new TextArea
11766  * @param {Object} config The config object
11767  */
11768
11769 Roo.bootstrap.TextArea = function(config){
11770     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11771    
11772 };
11773
11774 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11775      
11776     cols : false,
11777     rows : 5,
11778     readOnly : false,
11779     warp : 'soft',
11780     resize : false,
11781     value: false,
11782     html: false,
11783     
11784     getAutoCreate : function(){
11785         
11786         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11787         
11788         var id = Roo.id();
11789         
11790         var cfg = {};
11791         
11792         if(this.inputType != 'hidden'){
11793             cfg.cls = 'form-group' //input-group
11794         }
11795         
11796         var input =  {
11797             tag: 'textarea',
11798             id : id,
11799             warp : this.warp,
11800             rows : this.rows,
11801             value : this.value || '',
11802             html: this.html || '',
11803             cls : 'form-control',
11804             placeholder : this.placeholder || '' 
11805             
11806         };
11807         
11808         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11809             input.maxLength = this.maxLength;
11810         }
11811         
11812         if(this.resize){
11813             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11814         }
11815         
11816         if(this.cols){
11817             input.cols = this.cols;
11818         }
11819         
11820         if (this.readOnly) {
11821             input.readonly = true;
11822         }
11823         
11824         if (this.name) {
11825             input.name = this.name;
11826         }
11827         
11828         if (this.size) {
11829             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11830         }
11831         
11832         var settings=this;
11833         ['xs','sm','md','lg'].map(function(size){
11834             if (settings[size]) {
11835                 cfg.cls += ' col-' + size + '-' + settings[size];
11836             }
11837         });
11838         
11839         var inputblock = input;
11840         
11841         if(this.hasFeedback && !this.allowBlank){
11842             
11843             var feedback = {
11844                 tag: 'span',
11845                 cls: 'glyphicon form-control-feedback'
11846             };
11847
11848             inputblock = {
11849                 cls : 'has-feedback',
11850                 cn :  [
11851                     input,
11852                     feedback
11853                 ] 
11854             };  
11855         }
11856         
11857         
11858         if (this.before || this.after) {
11859             
11860             inputblock = {
11861                 cls : 'input-group',
11862                 cn :  [] 
11863             };
11864             if (this.before) {
11865                 inputblock.cn.push({
11866                     tag :'span',
11867                     cls : 'input-group-addon',
11868                     html : this.before
11869                 });
11870             }
11871             
11872             inputblock.cn.push(input);
11873             
11874             if(this.hasFeedback && !this.allowBlank){
11875                 inputblock.cls += ' has-feedback';
11876                 inputblock.cn.push(feedback);
11877             }
11878             
11879             if (this.after) {
11880                 inputblock.cn.push({
11881                     tag :'span',
11882                     cls : 'input-group-addon',
11883                     html : this.after
11884                 });
11885             }
11886             
11887         }
11888         
11889         if (align ==='left' && this.fieldLabel.length) {
11890             cfg.cn = [
11891                 {
11892                     tag: 'label',
11893                     'for' :  id,
11894                     cls : 'control-label',
11895                     html : this.fieldLabel
11896                 },
11897                 {
11898                     cls : "",
11899                     cn: [
11900                         inputblock
11901                     ]
11902                 }
11903
11904             ];
11905             
11906             if(this.labelWidth > 12){
11907                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11908             }
11909
11910             if(this.labelWidth < 13 && this.labelmd == 0){
11911                 this.labelmd = this.labelWidth;
11912             }
11913
11914             if(this.labellg > 0){
11915                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11916                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11917             }
11918
11919             if(this.labelmd > 0){
11920                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11921                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11922             }
11923
11924             if(this.labelsm > 0){
11925                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11926                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11927             }
11928
11929             if(this.labelxs > 0){
11930                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11931                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11932             }
11933             
11934         } else if ( this.fieldLabel.length) {
11935             cfg.cn = [
11936
11937                {
11938                    tag: 'label',
11939                    //cls : 'input-group-addon',
11940                    html : this.fieldLabel
11941
11942                },
11943
11944                inputblock
11945
11946            ];
11947
11948         } else {
11949
11950             cfg.cn = [
11951
11952                 inputblock
11953
11954             ];
11955                 
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         return cfg;
11963         
11964     },
11965     /**
11966      * return the real textarea element.
11967      */
11968     inputEl: function ()
11969     {
11970         return this.el.select('textarea.form-control',true).first();
11971     },
11972     
11973     /**
11974      * Clear any invalid styles/messages for this field
11975      */
11976     clearInvalid : function()
11977     {
11978         
11979         if(!this.el || this.preventMark){ // not rendered
11980             return;
11981         }
11982         
11983         var label = this.el.select('label', true).first();
11984         var icon = this.el.select('i.fa-star', true).first();
11985         
11986         if(label && icon){
11987             icon.remove();
11988         }
11989         this.el.removeClass( this.validClass);
11990         this.inputEl().removeClass('is-invalid');
11991          
11992         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11993             
11994             var feedback = this.el.select('.form-control-feedback', true).first();
11995             
11996             if(feedback){
11997                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11998             }
11999             
12000         }
12001         
12002         this.fireEvent('valid', this);
12003     },
12004     
12005      /**
12006      * Mark this field as valid
12007      */
12008     markValid : function()
12009     {
12010         if(!this.el  || this.preventMark){ // not rendered
12011             return;
12012         }
12013         
12014         this.el.removeClass([this.invalidClass, this.validClass]);
12015         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12016         
12017         var feedback = this.el.select('.form-control-feedback', true).first();
12018             
12019         if(feedback){
12020             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12021         }
12022
12023         if(this.disabled || this.allowBlank){
12024             return;
12025         }
12026         
12027         var label = this.el.select('label', true).first();
12028         var icon = this.el.select('i.fa-star', true).first();
12029         
12030         if(label && icon){
12031             icon.remove();
12032         }
12033         if (Roo.bootstrap.version == 3) {
12034             this.el.addClass(this.validClass);
12035         } else {
12036             this.inputEl().addClass('is-valid');
12037         }
12038         
12039         
12040         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12041             
12042             var feedback = this.el.select('.form-control-feedback', true).first();
12043             
12044             if(feedback){
12045                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12046                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12047             }
12048             
12049         }
12050         
12051         this.fireEvent('valid', this);
12052     },
12053     
12054      /**
12055      * Mark this field as invalid
12056      * @param {String} msg The validation message
12057      */
12058     markInvalid : function(msg)
12059     {
12060         if(!this.el  || this.preventMark){ // not rendered
12061             return;
12062         }
12063         
12064         this.el.removeClass([this.invalidClass, this.validClass]);
12065         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12066         
12067         var feedback = this.el.select('.form-control-feedback', true).first();
12068             
12069         if(feedback){
12070             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12071         }
12072
12073         if(this.disabled || this.allowBlank){
12074             return;
12075         }
12076         
12077         var label = this.el.select('label', true).first();
12078         var icon = this.el.select('i.fa-star', true).first();
12079         
12080         if(!this.getValue().length && label && !icon){
12081             this.el.createChild({
12082                 tag : 'i',
12083                 cls : 'text-danger fa fa-lg fa-star',
12084                 tooltip : 'This field is required',
12085                 style : 'margin-right:5px;'
12086             }, label, true);
12087         }
12088         
12089         if (Roo.bootstrap.version == 3) {
12090             this.el.addClass(this.invalidClass);
12091         } else {
12092             this.inputEl().addClass('is-invalid');
12093         }
12094         
12095         // fixme ... this may be depricated need to test..
12096         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12097             
12098             var feedback = this.el.select('.form-control-feedback', true).first();
12099             
12100             if(feedback){
12101                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12102                 
12103                 if(this.getValue().length || this.forceFeedback){
12104                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12105                 }
12106                 
12107             }
12108             
12109         }
12110         
12111         this.fireEvent('invalid', this, msg);
12112     }
12113 });
12114
12115  
12116 /*
12117  * - LGPL
12118  *
12119  * trigger field - base class for combo..
12120  * 
12121  */
12122  
12123 /**
12124  * @class Roo.bootstrap.TriggerField
12125  * @extends Roo.bootstrap.Input
12126  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12127  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12128  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12129  * for which you can provide a custom implementation.  For example:
12130  * <pre><code>
12131 var trigger = new Roo.bootstrap.TriggerField();
12132 trigger.onTriggerClick = myTriggerFn;
12133 trigger.applyTo('my-field');
12134 </code></pre>
12135  *
12136  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12137  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12138  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12139  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12140  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12141
12142  * @constructor
12143  * Create a new TriggerField.
12144  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12145  * to the base TextField)
12146  */
12147 Roo.bootstrap.TriggerField = function(config){
12148     this.mimicing = false;
12149     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12150 };
12151
12152 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12153     /**
12154      * @cfg {String} triggerClass A CSS class to apply to the trigger
12155      */
12156      /**
12157      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12158      */
12159     hideTrigger:false,
12160
12161     /**
12162      * @cfg {Boolean} removable (true|false) special filter default false
12163      */
12164     removable : false,
12165     
12166     /** @cfg {Boolean} grow @hide */
12167     /** @cfg {Number} growMin @hide */
12168     /** @cfg {Number} growMax @hide */
12169
12170     /**
12171      * @hide 
12172      * @method
12173      */
12174     autoSize: Roo.emptyFn,
12175     // private
12176     monitorTab : true,
12177     // private
12178     deferHeight : true,
12179
12180     
12181     actionMode : 'wrap',
12182     
12183     caret : false,
12184     
12185     
12186     getAutoCreate : function(){
12187        
12188         var align = this.labelAlign || this.parentLabelAlign();
12189         
12190         var id = Roo.id();
12191         
12192         var cfg = {
12193             cls: 'form-group' //input-group
12194         };
12195         
12196         
12197         var input =  {
12198             tag: 'input',
12199             id : id,
12200             type : this.inputType,
12201             cls : 'form-control',
12202             autocomplete: 'new-password',
12203             placeholder : this.placeholder || '' 
12204             
12205         };
12206         if (this.name) {
12207             input.name = this.name;
12208         }
12209         if (this.size) {
12210             input.cls += ' input-' + this.size;
12211         }
12212         
12213         if (this.disabled) {
12214             input.disabled=true;
12215         }
12216         
12217         var inputblock = input;
12218         
12219         if(this.hasFeedback && !this.allowBlank){
12220             
12221             var feedback = {
12222                 tag: 'span',
12223                 cls: 'glyphicon form-control-feedback'
12224             };
12225             
12226             if(this.removable && !this.editable  ){
12227                 inputblock = {
12228                     cls : 'has-feedback',
12229                     cn :  [
12230                         inputblock,
12231                         {
12232                             tag: 'button',
12233                             html : 'x',
12234                             cls : 'roo-combo-removable-btn close'
12235                         },
12236                         feedback
12237                     ] 
12238                 };
12239             } else {
12240                 inputblock = {
12241                     cls : 'has-feedback',
12242                     cn :  [
12243                         inputblock,
12244                         feedback
12245                     ] 
12246                 };
12247             }
12248
12249         } else {
12250             if(this.removable && !this.editable ){
12251                 inputblock = {
12252                     cls : 'roo-removable',
12253                     cn :  [
12254                         inputblock,
12255                         {
12256                             tag: 'button',
12257                             html : 'x',
12258                             cls : 'roo-combo-removable-btn close'
12259                         }
12260                     ] 
12261                 };
12262             }
12263         }
12264         
12265         if (this.before || this.after) {
12266             
12267             inputblock = {
12268                 cls : 'input-group',
12269                 cn :  [] 
12270             };
12271             if (this.before) {
12272                 inputblock.cn.push({
12273                     tag :'span',
12274                     cls : 'input-group-addon input-group-prepend input-group-text',
12275                     html : this.before
12276                 });
12277             }
12278             
12279             inputblock.cn.push(input);
12280             
12281             if(this.hasFeedback && !this.allowBlank){
12282                 inputblock.cls += ' has-feedback';
12283                 inputblock.cn.push(feedback);
12284             }
12285             
12286             if (this.after) {
12287                 inputblock.cn.push({
12288                     tag :'span',
12289                     cls : 'input-group-addon input-group-append input-group-text',
12290                     html : this.after
12291                 });
12292             }
12293             
12294         };
12295         
12296       
12297         
12298         var ibwrap = inputblock;
12299         
12300         if(this.multiple){
12301             ibwrap = {
12302                 tag: 'ul',
12303                 cls: 'roo-select2-choices',
12304                 cn:[
12305                     {
12306                         tag: 'li',
12307                         cls: 'roo-select2-search-field',
12308                         cn: [
12309
12310                             inputblock
12311                         ]
12312                     }
12313                 ]
12314             };
12315                 
12316         }
12317         
12318         var combobox = {
12319             cls: 'roo-select2-container input-group',
12320             cn: [
12321                  {
12322                     tag: 'input',
12323                     type : 'hidden',
12324                     cls: 'form-hidden-field'
12325                 },
12326                 ibwrap
12327             ]
12328         };
12329         
12330         if(!this.multiple && this.showToggleBtn){
12331             
12332             var caret = {
12333                         tag: 'span',
12334                         cls: 'caret'
12335              };
12336             if (this.caret != false) {
12337                 caret = {
12338                      tag: 'i',
12339                      cls: 'fa fa-' + this.caret
12340                 };
12341                 
12342             }
12343             
12344             combobox.cn.push({
12345                 tag :'span',
12346                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12347                 cn : [
12348                     Roo.bootstrap.version == 3 ? caret : '',
12349                     {
12350                         tag: 'span',
12351                         cls: 'combobox-clear',
12352                         cn  : [
12353                             {
12354                                 tag : 'i',
12355                                 cls: 'icon-remove'
12356                             }
12357                         ]
12358                     }
12359                 ]
12360
12361             })
12362         }
12363         
12364         if(this.multiple){
12365             combobox.cls += ' roo-select2-container-multi';
12366         }
12367          var indicator = {
12368             tag : 'i',
12369             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12370             tooltip : 'This field is required'
12371         };
12372         if (Roo.bootstrap.version == 4) {
12373             indicator = {
12374                 tag : 'i',
12375                 style : 'display:none'
12376             };
12377         }
12378         
12379         
12380         if (align ==='left' && this.fieldLabel.length) {
12381             
12382             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12383
12384             cfg.cn = [
12385                 indicator,
12386                 {
12387                     tag: 'label',
12388                     'for' :  id,
12389                     cls : 'control-label',
12390                     html : this.fieldLabel
12391
12392                 },
12393                 {
12394                     cls : "", 
12395                     cn: [
12396                         combobox
12397                     ]
12398                 }
12399
12400             ];
12401             
12402             var labelCfg = cfg.cn[1];
12403             var contentCfg = cfg.cn[2];
12404             
12405             if(this.indicatorpos == 'right'){
12406                 cfg.cn = [
12407                     {
12408                         tag: 'label',
12409                         'for' :  id,
12410                         cls : 'control-label',
12411                         cn : [
12412                             {
12413                                 tag : 'span',
12414                                 html : this.fieldLabel
12415                             },
12416                             indicator
12417                         ]
12418                     },
12419                     {
12420                         cls : "", 
12421                         cn: [
12422                             combobox
12423                         ]
12424                     }
12425
12426                 ];
12427                 
12428                 labelCfg = cfg.cn[0];
12429                 contentCfg = cfg.cn[1];
12430             }
12431             
12432             if(this.labelWidth > 12){
12433                 labelCfg.style = "width: " + this.labelWidth + 'px';
12434             }
12435             
12436             if(this.labelWidth < 13 && this.labelmd == 0){
12437                 this.labelmd = this.labelWidth;
12438             }
12439             
12440             if(this.labellg > 0){
12441                 labelCfg.cls += ' col-lg-' + this.labellg;
12442                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12443             }
12444             
12445             if(this.labelmd > 0){
12446                 labelCfg.cls += ' col-md-' + this.labelmd;
12447                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12448             }
12449             
12450             if(this.labelsm > 0){
12451                 labelCfg.cls += ' col-sm-' + this.labelsm;
12452                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12453             }
12454             
12455             if(this.labelxs > 0){
12456                 labelCfg.cls += ' col-xs-' + this.labelxs;
12457                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12458             }
12459             
12460         } else if ( this.fieldLabel.length) {
12461 //                Roo.log(" label");
12462             cfg.cn = [
12463                 indicator,
12464                {
12465                    tag: 'label',
12466                    //cls : 'input-group-addon',
12467                    html : this.fieldLabel
12468
12469                },
12470
12471                combobox
12472
12473             ];
12474             
12475             if(this.indicatorpos == 'right'){
12476                 
12477                 cfg.cn = [
12478                     {
12479                        tag: 'label',
12480                        cn : [
12481                            {
12482                                tag : 'span',
12483                                html : this.fieldLabel
12484                            },
12485                            indicator
12486                        ]
12487
12488                     },
12489                     combobox
12490
12491                 ];
12492
12493             }
12494
12495         } else {
12496             
12497 //                Roo.log(" no label && no align");
12498                 cfg = combobox
12499                      
12500                 
12501         }
12502         
12503         var settings=this;
12504         ['xs','sm','md','lg'].map(function(size){
12505             if (settings[size]) {
12506                 cfg.cls += ' col-' + size + '-' + settings[size];
12507             }
12508         });
12509         
12510         return cfg;
12511         
12512     },
12513     
12514     
12515     
12516     // private
12517     onResize : function(w, h){
12518 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12519 //        if(typeof w == 'number'){
12520 //            var x = w - this.trigger.getWidth();
12521 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12522 //            this.trigger.setStyle('left', x+'px');
12523 //        }
12524     },
12525
12526     // private
12527     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12528
12529     // private
12530     getResizeEl : function(){
12531         return this.inputEl();
12532     },
12533
12534     // private
12535     getPositionEl : function(){
12536         return this.inputEl();
12537     },
12538
12539     // private
12540     alignErrorIcon : function(){
12541         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12542     },
12543
12544     // private
12545     initEvents : function(){
12546         
12547         this.createList();
12548         
12549         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12550         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12551         if(!this.multiple && this.showToggleBtn){
12552             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12553             if(this.hideTrigger){
12554                 this.trigger.setDisplayed(false);
12555             }
12556             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12557         }
12558         
12559         if(this.multiple){
12560             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12561         }
12562         
12563         if(this.removable && !this.editable && !this.tickable){
12564             var close = this.closeTriggerEl();
12565             
12566             if(close){
12567                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12568                 close.on('click', this.removeBtnClick, this, close);
12569             }
12570         }
12571         
12572         //this.trigger.addClassOnOver('x-form-trigger-over');
12573         //this.trigger.addClassOnClick('x-form-trigger-click');
12574         
12575         //if(!this.width){
12576         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12577         //}
12578     },
12579     
12580     closeTriggerEl : function()
12581     {
12582         var close = this.el.select('.roo-combo-removable-btn', true).first();
12583         return close ? close : false;
12584     },
12585     
12586     removeBtnClick : function(e, h, el)
12587     {
12588         e.preventDefault();
12589         
12590         if(this.fireEvent("remove", this) !== false){
12591             this.reset();
12592             this.fireEvent("afterremove", this)
12593         }
12594     },
12595     
12596     createList : function()
12597     {
12598         this.list = Roo.get(document.body).createChild({
12599             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12600             cls: 'typeahead typeahead-long dropdown-menu shadow',
12601             style: 'display:none'
12602         });
12603         
12604         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12605         
12606     },
12607
12608     // private
12609     initTrigger : function(){
12610        
12611     },
12612
12613     // private
12614     onDestroy : function(){
12615         if(this.trigger){
12616             this.trigger.removeAllListeners();
12617           //  this.trigger.remove();
12618         }
12619         //if(this.wrap){
12620         //    this.wrap.remove();
12621         //}
12622         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12623     },
12624
12625     // private
12626     onFocus : function(){
12627         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12628         /*
12629         if(!this.mimicing){
12630             this.wrap.addClass('x-trigger-wrap-focus');
12631             this.mimicing = true;
12632             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12633             if(this.monitorTab){
12634                 this.el.on("keydown", this.checkTab, this);
12635             }
12636         }
12637         */
12638     },
12639
12640     // private
12641     checkTab : function(e){
12642         if(e.getKey() == e.TAB){
12643             this.triggerBlur();
12644         }
12645     },
12646
12647     // private
12648     onBlur : function(){
12649         // do nothing
12650     },
12651
12652     // private
12653     mimicBlur : function(e, t){
12654         /*
12655         if(!this.wrap.contains(t) && this.validateBlur()){
12656             this.triggerBlur();
12657         }
12658         */
12659     },
12660
12661     // private
12662     triggerBlur : function(){
12663         this.mimicing = false;
12664         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12665         if(this.monitorTab){
12666             this.el.un("keydown", this.checkTab, this);
12667         }
12668         //this.wrap.removeClass('x-trigger-wrap-focus');
12669         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12670     },
12671
12672     // private
12673     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12674     validateBlur : function(e, t){
12675         return true;
12676     },
12677
12678     // private
12679     onDisable : function(){
12680         this.inputEl().dom.disabled = true;
12681         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12682         //if(this.wrap){
12683         //    this.wrap.addClass('x-item-disabled');
12684         //}
12685     },
12686
12687     // private
12688     onEnable : function(){
12689         this.inputEl().dom.disabled = false;
12690         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12691         //if(this.wrap){
12692         //    this.el.removeClass('x-item-disabled');
12693         //}
12694     },
12695
12696     // private
12697     onShow : function(){
12698         var ae = this.getActionEl();
12699         
12700         if(ae){
12701             ae.dom.style.display = '';
12702             ae.dom.style.visibility = 'visible';
12703         }
12704     },
12705
12706     // private
12707     
12708     onHide : function(){
12709         var ae = this.getActionEl();
12710         ae.dom.style.display = 'none';
12711     },
12712
12713     /**
12714      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12715      * by an implementing function.
12716      * @method
12717      * @param {EventObject} e
12718      */
12719     onTriggerClick : Roo.emptyFn
12720 });
12721  
12722 /*
12723 * Licence: LGPL
12724 */
12725
12726 /**
12727  * @class Roo.bootstrap.CardUploader
12728  * @extends Roo.bootstrap.Button
12729  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12730  * @cfg {Number} errorTimeout default 3000
12731  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12732  * @cfg {Array}  html The button text.
12733
12734  *
12735  * @constructor
12736  * Create a new CardUploader
12737  * @param {Object} config The config object
12738  */
12739
12740 Roo.bootstrap.CardUploader = function(config){
12741     
12742  
12743     
12744     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12745     
12746     
12747     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12748         return r.data.id
12749      });
12750     
12751      this.addEvents({
12752          // raw events
12753         /**
12754          * @event preview
12755          * When a image is clicked on - and needs to display a slideshow or similar..
12756          * @param {Roo.bootstrap.Card} this
12757          * @param {Object} The image information data 
12758          *
12759          */
12760         'preview' : true,
12761          /**
12762          * @event download
12763          * When a the download link is clicked
12764          * @param {Roo.bootstrap.Card} this
12765          * @param {Object} The image information data  contains 
12766          */
12767         'download' : true
12768         
12769     });
12770 };
12771  
12772 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12773     
12774      
12775     errorTimeout : 3000,
12776      
12777     images : false,
12778    
12779     fileCollection : false,
12780     allowBlank : true,
12781     
12782     getAutoCreate : function()
12783     {
12784         
12785         var cfg =  {
12786             cls :'form-group' ,
12787             cn : [
12788                
12789                 {
12790                     tag: 'label',
12791                    //cls : 'input-group-addon',
12792                     html : this.fieldLabel
12793
12794                 },
12795
12796                 {
12797                     tag: 'input',
12798                     type : 'hidden',
12799                     name : this.name,
12800                     value : this.value,
12801                     cls : 'd-none  form-control'
12802                 },
12803                 
12804                 {
12805                     tag: 'input',
12806                     multiple : 'multiple',
12807                     type : 'file',
12808                     cls : 'd-none  roo-card-upload-selector'
12809                 },
12810                 
12811                 {
12812                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12813                 },
12814                 {
12815                     cls : 'card-columns roo-card-uploader-container'
12816                 }
12817
12818             ]
12819         };
12820            
12821          
12822         return cfg;
12823     },
12824     
12825     getChildContainer : function() /// what children are added to.
12826     {
12827         return this.containerEl;
12828     },
12829    
12830     getButtonContainer : function() /// what children are added to.
12831     {
12832         return this.el.select(".roo-card-uploader-button-container").first();
12833     },
12834    
12835     initEvents : function()
12836     {
12837         
12838         Roo.bootstrap.Input.prototype.initEvents.call(this);
12839         
12840         var t = this;
12841         this.addxtype({
12842             xns: Roo.bootstrap,
12843
12844             xtype : 'Button',
12845             container_method : 'getButtonContainer' ,            
12846             html :  this.html, // fix changable?
12847             cls : 'w-100 ',
12848             listeners : {
12849                 'click' : function(btn, e) {
12850                     t.onClick(e);
12851                 }
12852             }
12853         });
12854         
12855         
12856         
12857         
12858         this.urlAPI = (window.createObjectURL && window) || 
12859                                 (window.URL && URL.revokeObjectURL && URL) || 
12860                                 (window.webkitURL && webkitURL);
12861                         
12862          
12863          
12864          
12865         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12866         
12867         this.selectorEl.on('change', this.onFileSelected, this);
12868         if (this.images) {
12869             var t = this;
12870             this.images.forEach(function(img) {
12871                 t.addCard(img)
12872             });
12873             this.images = false;
12874         }
12875         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12876          
12877        
12878     },
12879     
12880    
12881     onClick : function(e)
12882     {
12883         e.preventDefault();
12884          
12885         this.selectorEl.dom.click();
12886          
12887     },
12888     
12889     onFileSelected : function(e)
12890     {
12891         e.preventDefault();
12892         
12893         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12894             return;
12895         }
12896         
12897         Roo.each(this.selectorEl.dom.files, function(file){    
12898             this.addFile(file);
12899         }, this);
12900          
12901     },
12902     
12903       
12904     
12905       
12906     
12907     addFile : function(file)
12908     {
12909            
12910         if(typeof(file) === 'string'){
12911             throw "Add file by name?"; // should not happen
12912             return;
12913         }
12914         
12915         if(!file || !this.urlAPI){
12916             return;
12917         }
12918         
12919         // file;
12920         // file.type;
12921         
12922         var _this = this;
12923         
12924         
12925         var url = _this.urlAPI.createObjectURL( file);
12926            
12927         this.addCard({
12928             id : Roo.bootstrap.CardUploader.ID--,
12929             is_uploaded : false,
12930             src : url,
12931             srcfile : file,
12932             title : file.name,
12933             mimetype : file.type,
12934             preview : false,
12935             is_deleted : 0
12936         });
12937         
12938     },
12939     
12940     /**
12941      * addCard - add an Attachment to the uploader
12942      * @param data - the data about the image to upload
12943      *
12944      * {
12945           id : 123
12946           title : "Title of file",
12947           is_uploaded : false,
12948           src : "http://.....",
12949           srcfile : { the File upload object },
12950           mimetype : file.type,
12951           preview : false,
12952           is_deleted : 0
12953           .. any other data...
12954         }
12955      *
12956      * 
12957     */
12958     
12959     addCard : function (data)
12960     {
12961         // hidden input element?
12962         // if the file is not an image...
12963         //then we need to use something other that and header_image
12964         var t = this;
12965         //   remove.....
12966         var footer = [
12967             {
12968                 xns : Roo.bootstrap,
12969                 xtype : 'CardFooter',
12970                  items: [
12971                     {
12972                         xns : Roo.bootstrap,
12973                         xtype : 'Element',
12974                         cls : 'd-flex',
12975                         items : [
12976                             
12977                             {
12978                                 xns : Roo.bootstrap,
12979                                 xtype : 'Button',
12980                                 html : String.format("<small>{0}</small>", data.title),
12981                                 cls : 'col-10 text-left',
12982                                 size: 'sm',
12983                                 weight: 'link',
12984                                 fa : 'download',
12985                                 listeners : {
12986                                     click : function() {
12987                                      
12988                                         t.fireEvent( "download", t, data );
12989                                     }
12990                                 }
12991                             },
12992                           
12993                             {
12994                                 xns : Roo.bootstrap,
12995                                 xtype : 'Button',
12996                                 style: 'max-height: 28px; ',
12997                                 size : 'sm',
12998                                 weight: 'danger',
12999                                 cls : 'col-2',
13000                                 fa : 'times',
13001                                 listeners : {
13002                                     click : function() {
13003                                         t.removeCard(data.id)
13004                                     }
13005                                 }
13006                             }
13007                         ]
13008                     }
13009                     
13010                 ] 
13011             }
13012             
13013         ];
13014         
13015         var cn = this.addxtype(
13016             {
13017                  
13018                 xns : Roo.bootstrap,
13019                 xtype : 'Card',
13020                 closeable : true,
13021                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13022                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13023                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13024                 data : data,
13025                 html : false,
13026                  
13027                 items : footer,
13028                 initEvents : function() {
13029                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13030                     var card = this;
13031                     this.imgEl = this.el.select('.card-img-top').first();
13032                     if (this.imgEl) {
13033                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13034                         this.imgEl.set({ 'pointer' : 'cursor' });
13035                                   
13036                     }
13037                     this.getCardFooter().addClass('p-1');
13038                     
13039                   
13040                 }
13041                 
13042             }
13043         );
13044         // dont' really need ot update items.
13045         // this.items.push(cn);
13046         this.fileCollection.add(cn);
13047         
13048         if (!data.srcfile) {
13049             this.updateInput();
13050             return;
13051         }
13052             
13053         var _t = this;
13054         var reader = new FileReader();
13055         reader.addEventListener("load", function() {  
13056             data.srcdata =  reader.result;
13057             _t.updateInput();
13058         });
13059         reader.readAsDataURL(data.srcfile);
13060         
13061         
13062         
13063     },
13064     removeCard : function(id)
13065     {
13066         
13067         var card  = this.fileCollection.get(id);
13068         card.data.is_deleted = 1;
13069         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13070         //this.fileCollection.remove(card);
13071         //this.items = this.items.filter(function(e) { return e != card });
13072         // dont' really need ot update items.
13073         card.el.dom.parentNode.removeChild(card.el.dom);
13074         this.updateInput();
13075
13076         
13077     },
13078     reset: function()
13079     {
13080         this.fileCollection.each(function(card) {
13081             if (card.el.dom && card.el.dom.parentNode) {
13082                 card.el.dom.parentNode.removeChild(card.el.dom);
13083             }
13084         });
13085         this.fileCollection.clear();
13086         this.updateInput();
13087     },
13088     
13089     updateInput : function()
13090     {
13091          var data = [];
13092         this.fileCollection.each(function(e) {
13093             data.push(e.data);
13094             
13095         });
13096         this.inputEl().dom.value = JSON.stringify(data);
13097         
13098         
13099         
13100     }
13101     
13102     
13103 });
13104
13105
13106 Roo.bootstrap.CardUploader.ID = -1;/*
13107  * Based on:
13108  * Ext JS Library 1.1.1
13109  * Copyright(c) 2006-2007, Ext JS, LLC.
13110  *
13111  * Originally Released Under LGPL - original licence link has changed is not relivant.
13112  *
13113  * Fork - LGPL
13114  * <script type="text/javascript">
13115  */
13116
13117
13118 /**
13119  * @class Roo.data.SortTypes
13120  * @singleton
13121  * Defines the default sorting (casting?) comparison functions used when sorting data.
13122  */
13123 Roo.data.SortTypes = {
13124     /**
13125      * Default sort that does nothing
13126      * @param {Mixed} s The value being converted
13127      * @return {Mixed} The comparison value
13128      */
13129     none : function(s){
13130         return s;
13131     },
13132     
13133     /**
13134      * The regular expression used to strip tags
13135      * @type {RegExp}
13136      * @property
13137      */
13138     stripTagsRE : /<\/?[^>]+>/gi,
13139     
13140     /**
13141      * Strips all HTML tags to sort on text only
13142      * @param {Mixed} s The value being converted
13143      * @return {String} The comparison value
13144      */
13145     asText : function(s){
13146         return String(s).replace(this.stripTagsRE, "");
13147     },
13148     
13149     /**
13150      * Strips all HTML tags to sort on text only - Case insensitive
13151      * @param {Mixed} s The value being converted
13152      * @return {String} The comparison value
13153      */
13154     asUCText : function(s){
13155         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13156     },
13157     
13158     /**
13159      * Case insensitive string
13160      * @param {Mixed} s The value being converted
13161      * @return {String} The comparison value
13162      */
13163     asUCString : function(s) {
13164         return String(s).toUpperCase();
13165     },
13166     
13167     /**
13168      * Date sorting
13169      * @param {Mixed} s The value being converted
13170      * @return {Number} The comparison value
13171      */
13172     asDate : function(s) {
13173         if(!s){
13174             return 0;
13175         }
13176         if(s instanceof Date){
13177             return s.getTime();
13178         }
13179         return Date.parse(String(s));
13180     },
13181     
13182     /**
13183      * Float sorting
13184      * @param {Mixed} s The value being converted
13185      * @return {Float} The comparison value
13186      */
13187     asFloat : function(s) {
13188         var val = parseFloat(String(s).replace(/,/g, ""));
13189         if(isNaN(val)) {
13190             val = 0;
13191         }
13192         return val;
13193     },
13194     
13195     /**
13196      * Integer sorting
13197      * @param {Mixed} s The value being converted
13198      * @return {Number} The comparison value
13199      */
13200     asInt : function(s) {
13201         var val = parseInt(String(s).replace(/,/g, ""));
13202         if(isNaN(val)) {
13203             val = 0;
13204         }
13205         return val;
13206     }
13207 };/*
13208  * Based on:
13209  * Ext JS Library 1.1.1
13210  * Copyright(c) 2006-2007, Ext JS, LLC.
13211  *
13212  * Originally Released Under LGPL - original licence link has changed is not relivant.
13213  *
13214  * Fork - LGPL
13215  * <script type="text/javascript">
13216  */
13217
13218 /**
13219 * @class Roo.data.Record
13220  * Instances of this class encapsulate both record <em>definition</em> information, and record
13221  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13222  * to access Records cached in an {@link Roo.data.Store} object.<br>
13223  * <p>
13224  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13225  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13226  * objects.<br>
13227  * <p>
13228  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13229  * @constructor
13230  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13231  * {@link #create}. The parameters are the same.
13232  * @param {Array} data An associative Array of data values keyed by the field name.
13233  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13234  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13235  * not specified an integer id is generated.
13236  */
13237 Roo.data.Record = function(data, id){
13238     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13239     this.data = data;
13240 };
13241
13242 /**
13243  * Generate a constructor for a specific record layout.
13244  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13245  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13246  * Each field definition object may contain the following properties: <ul>
13247  * <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,
13248  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13249  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13250  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13251  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13252  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13253  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13254  * this may be omitted.</p></li>
13255  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13256  * <ul><li>auto (Default, implies no conversion)</li>
13257  * <li>string</li>
13258  * <li>int</li>
13259  * <li>float</li>
13260  * <li>boolean</li>
13261  * <li>date</li></ul></p></li>
13262  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13263  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13264  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13265  * by the Reader into an object that will be stored in the Record. It is passed the
13266  * following parameters:<ul>
13267  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13268  * </ul></p></li>
13269  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13270  * </ul>
13271  * <br>usage:<br><pre><code>
13272 var TopicRecord = Roo.data.Record.create(
13273     {name: 'title', mapping: 'topic_title'},
13274     {name: 'author', mapping: 'username'},
13275     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13276     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13277     {name: 'lastPoster', mapping: 'user2'},
13278     {name: 'excerpt', mapping: 'post_text'}
13279 );
13280
13281 var myNewRecord = new TopicRecord({
13282     title: 'Do my job please',
13283     author: 'noobie',
13284     totalPosts: 1,
13285     lastPost: new Date(),
13286     lastPoster: 'Animal',
13287     excerpt: 'No way dude!'
13288 });
13289 myStore.add(myNewRecord);
13290 </code></pre>
13291  * @method create
13292  * @static
13293  */
13294 Roo.data.Record.create = function(o){
13295     var f = function(){
13296         f.superclass.constructor.apply(this, arguments);
13297     };
13298     Roo.extend(f, Roo.data.Record);
13299     var p = f.prototype;
13300     p.fields = new Roo.util.MixedCollection(false, function(field){
13301         return field.name;
13302     });
13303     for(var i = 0, len = o.length; i < len; i++){
13304         p.fields.add(new Roo.data.Field(o[i]));
13305     }
13306     f.getField = function(name){
13307         return p.fields.get(name);  
13308     };
13309     return f;
13310 };
13311
13312 Roo.data.Record.AUTO_ID = 1000;
13313 Roo.data.Record.EDIT = 'edit';
13314 Roo.data.Record.REJECT = 'reject';
13315 Roo.data.Record.COMMIT = 'commit';
13316
13317 Roo.data.Record.prototype = {
13318     /**
13319      * Readonly flag - true if this record has been modified.
13320      * @type Boolean
13321      */
13322     dirty : false,
13323     editing : false,
13324     error: null,
13325     modified: null,
13326
13327     // private
13328     join : function(store){
13329         this.store = store;
13330     },
13331
13332     /**
13333      * Set the named field to the specified value.
13334      * @param {String} name The name of the field to set.
13335      * @param {Object} value The value to set the field to.
13336      */
13337     set : function(name, value){
13338         if(this.data[name] == value){
13339             return;
13340         }
13341         this.dirty = true;
13342         if(!this.modified){
13343             this.modified = {};
13344         }
13345         if(typeof this.modified[name] == 'undefined'){
13346             this.modified[name] = this.data[name];
13347         }
13348         this.data[name] = value;
13349         if(!this.editing && this.store){
13350             this.store.afterEdit(this);
13351         }       
13352     },
13353
13354     /**
13355      * Get the value of the named field.
13356      * @param {String} name The name of the field to get the value of.
13357      * @return {Object} The value of the field.
13358      */
13359     get : function(name){
13360         return this.data[name]; 
13361     },
13362
13363     // private
13364     beginEdit : function(){
13365         this.editing = true;
13366         this.modified = {}; 
13367     },
13368
13369     // private
13370     cancelEdit : function(){
13371         this.editing = false;
13372         delete this.modified;
13373     },
13374
13375     // private
13376     endEdit : function(){
13377         this.editing = false;
13378         if(this.dirty && this.store){
13379             this.store.afterEdit(this);
13380         }
13381     },
13382
13383     /**
13384      * Usually called by the {@link Roo.data.Store} which owns the Record.
13385      * Rejects all changes made to the Record since either creation, or the last commit operation.
13386      * Modified fields are reverted to their original values.
13387      * <p>
13388      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13389      * of reject operations.
13390      */
13391     reject : function(){
13392         var m = this.modified;
13393         for(var n in m){
13394             if(typeof m[n] != "function"){
13395                 this.data[n] = m[n];
13396             }
13397         }
13398         this.dirty = false;
13399         delete this.modified;
13400         this.editing = false;
13401         if(this.store){
13402             this.store.afterReject(this);
13403         }
13404     },
13405
13406     /**
13407      * Usually called by the {@link Roo.data.Store} which owns the Record.
13408      * Commits all changes made to the Record since either creation, or the last commit operation.
13409      * <p>
13410      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13411      * of commit operations.
13412      */
13413     commit : function(){
13414         this.dirty = false;
13415         delete this.modified;
13416         this.editing = false;
13417         if(this.store){
13418             this.store.afterCommit(this);
13419         }
13420     },
13421
13422     // private
13423     hasError : function(){
13424         return this.error != null;
13425     },
13426
13427     // private
13428     clearError : function(){
13429         this.error = null;
13430     },
13431
13432     /**
13433      * Creates a copy of this record.
13434      * @param {String} id (optional) A new record id if you don't want to use this record's id
13435      * @return {Record}
13436      */
13437     copy : function(newId) {
13438         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13439     }
13440 };/*
13441  * Based on:
13442  * Ext JS Library 1.1.1
13443  * Copyright(c) 2006-2007, Ext JS, LLC.
13444  *
13445  * Originally Released Under LGPL - original licence link has changed is not relivant.
13446  *
13447  * Fork - LGPL
13448  * <script type="text/javascript">
13449  */
13450
13451
13452
13453 /**
13454  * @class Roo.data.Store
13455  * @extends Roo.util.Observable
13456  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13457  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13458  * <p>
13459  * 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
13460  * has no knowledge of the format of the data returned by the Proxy.<br>
13461  * <p>
13462  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13463  * instances from the data object. These records are cached and made available through accessor functions.
13464  * @constructor
13465  * Creates a new Store.
13466  * @param {Object} config A config object containing the objects needed for the Store to access data,
13467  * and read the data into Records.
13468  */
13469 Roo.data.Store = function(config){
13470     this.data = new Roo.util.MixedCollection(false);
13471     this.data.getKey = function(o){
13472         return o.id;
13473     };
13474     this.baseParams = {};
13475     // private
13476     this.paramNames = {
13477         "start" : "start",
13478         "limit" : "limit",
13479         "sort" : "sort",
13480         "dir" : "dir",
13481         "multisort" : "_multisort"
13482     };
13483
13484     if(config && config.data){
13485         this.inlineData = config.data;
13486         delete config.data;
13487     }
13488
13489     Roo.apply(this, config);
13490     
13491     if(this.reader){ // reader passed
13492         this.reader = Roo.factory(this.reader, Roo.data);
13493         this.reader.xmodule = this.xmodule || false;
13494         if(!this.recordType){
13495             this.recordType = this.reader.recordType;
13496         }
13497         if(this.reader.onMetaChange){
13498             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13499         }
13500     }
13501
13502     if(this.recordType){
13503         this.fields = this.recordType.prototype.fields;
13504     }
13505     this.modified = [];
13506
13507     this.addEvents({
13508         /**
13509          * @event datachanged
13510          * Fires when the data cache has changed, and a widget which is using this Store
13511          * as a Record cache should refresh its view.
13512          * @param {Store} this
13513          */
13514         datachanged : true,
13515         /**
13516          * @event metachange
13517          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13518          * @param {Store} this
13519          * @param {Object} meta The JSON metadata
13520          */
13521         metachange : true,
13522         /**
13523          * @event add
13524          * Fires when Records have been added to the Store
13525          * @param {Store} this
13526          * @param {Roo.data.Record[]} records The array of Records added
13527          * @param {Number} index The index at which the record(s) were added
13528          */
13529         add : true,
13530         /**
13531          * @event remove
13532          * Fires when a Record has been removed from the Store
13533          * @param {Store} this
13534          * @param {Roo.data.Record} record The Record that was removed
13535          * @param {Number} index The index at which the record was removed
13536          */
13537         remove : true,
13538         /**
13539          * @event update
13540          * Fires when a Record has been updated
13541          * @param {Store} this
13542          * @param {Roo.data.Record} record The Record that was updated
13543          * @param {String} operation The update operation being performed.  Value may be one of:
13544          * <pre><code>
13545  Roo.data.Record.EDIT
13546  Roo.data.Record.REJECT
13547  Roo.data.Record.COMMIT
13548          * </code></pre>
13549          */
13550         update : true,
13551         /**
13552          * @event clear
13553          * Fires when the data cache has been cleared.
13554          * @param {Store} this
13555          */
13556         clear : true,
13557         /**
13558          * @event beforeload
13559          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13560          * the load action will be canceled.
13561          * @param {Store} this
13562          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13563          */
13564         beforeload : true,
13565         /**
13566          * @event beforeloadadd
13567          * Fires after a new set of Records has been loaded.
13568          * @param {Store} this
13569          * @param {Roo.data.Record[]} records The Records that were loaded
13570          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13571          */
13572         beforeloadadd : true,
13573         /**
13574          * @event load
13575          * Fires after a new set of Records has been loaded, before they are added to the store.
13576          * @param {Store} this
13577          * @param {Roo.data.Record[]} records The Records that were loaded
13578          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13579          * @params {Object} return from reader
13580          */
13581         load : true,
13582         /**
13583          * @event loadexception
13584          * Fires if an exception occurs in the Proxy during loading.
13585          * Called with the signature of the Proxy's "loadexception" event.
13586          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13587          * 
13588          * @param {Proxy} 
13589          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13590          * @param {Object} load options 
13591          * @param {Object} jsonData from your request (normally this contains the Exception)
13592          */
13593         loadexception : true
13594     });
13595     
13596     if(this.proxy){
13597         this.proxy = Roo.factory(this.proxy, Roo.data);
13598         this.proxy.xmodule = this.xmodule || false;
13599         this.relayEvents(this.proxy,  ["loadexception"]);
13600     }
13601     this.sortToggle = {};
13602     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13603
13604     Roo.data.Store.superclass.constructor.call(this);
13605
13606     if(this.inlineData){
13607         this.loadData(this.inlineData);
13608         delete this.inlineData;
13609     }
13610 };
13611
13612 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13613      /**
13614     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13615     * without a remote query - used by combo/forms at present.
13616     */
13617     
13618     /**
13619     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13620     */
13621     /**
13622     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13623     */
13624     /**
13625     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13626     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13627     */
13628     /**
13629     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13630     * on any HTTP request
13631     */
13632     /**
13633     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13634     */
13635     /**
13636     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13637     */
13638     multiSort: false,
13639     /**
13640     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13641     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13642     */
13643     remoteSort : false,
13644
13645     /**
13646     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13647      * loaded or when a record is removed. (defaults to false).
13648     */
13649     pruneModifiedRecords : false,
13650
13651     // private
13652     lastOptions : null,
13653
13654     /**
13655      * Add Records to the Store and fires the add event.
13656      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13657      */
13658     add : function(records){
13659         records = [].concat(records);
13660         for(var i = 0, len = records.length; i < len; i++){
13661             records[i].join(this);
13662         }
13663         var index = this.data.length;
13664         this.data.addAll(records);
13665         this.fireEvent("add", this, records, index);
13666     },
13667
13668     /**
13669      * Remove a Record from the Store and fires the remove event.
13670      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13671      */
13672     remove : function(record){
13673         var index = this.data.indexOf(record);
13674         this.data.removeAt(index);
13675  
13676         if(this.pruneModifiedRecords){
13677             this.modified.remove(record);
13678         }
13679         this.fireEvent("remove", this, record, index);
13680     },
13681
13682     /**
13683      * Remove all Records from the Store and fires the clear event.
13684      */
13685     removeAll : function(){
13686         this.data.clear();
13687         if(this.pruneModifiedRecords){
13688             this.modified = [];
13689         }
13690         this.fireEvent("clear", this);
13691     },
13692
13693     /**
13694      * Inserts Records to the Store at the given index and fires the add event.
13695      * @param {Number} index The start index at which to insert the passed Records.
13696      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13697      */
13698     insert : function(index, records){
13699         records = [].concat(records);
13700         for(var i = 0, len = records.length; i < len; i++){
13701             this.data.insert(index, records[i]);
13702             records[i].join(this);
13703         }
13704         this.fireEvent("add", this, records, index);
13705     },
13706
13707     /**
13708      * Get the index within the cache of the passed Record.
13709      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13710      * @return {Number} The index of the passed Record. Returns -1 if not found.
13711      */
13712     indexOf : function(record){
13713         return this.data.indexOf(record);
13714     },
13715
13716     /**
13717      * Get the index within the cache of the Record with the passed id.
13718      * @param {String} id The id of the Record to find.
13719      * @return {Number} The index of the Record. Returns -1 if not found.
13720      */
13721     indexOfId : function(id){
13722         return this.data.indexOfKey(id);
13723     },
13724
13725     /**
13726      * Get the Record with the specified id.
13727      * @param {String} id The id of the Record to find.
13728      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13729      */
13730     getById : function(id){
13731         return this.data.key(id);
13732     },
13733
13734     /**
13735      * Get the Record at the specified index.
13736      * @param {Number} index The index of the Record to find.
13737      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13738      */
13739     getAt : function(index){
13740         return this.data.itemAt(index);
13741     },
13742
13743     /**
13744      * Returns a range of Records between specified indices.
13745      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13746      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13747      * @return {Roo.data.Record[]} An array of Records
13748      */
13749     getRange : function(start, end){
13750         return this.data.getRange(start, end);
13751     },
13752
13753     // private
13754     storeOptions : function(o){
13755         o = Roo.apply({}, o);
13756         delete o.callback;
13757         delete o.scope;
13758         this.lastOptions = o;
13759     },
13760
13761     /**
13762      * Loads the Record cache from the configured Proxy using the configured Reader.
13763      * <p>
13764      * If using remote paging, then the first load call must specify the <em>start</em>
13765      * and <em>limit</em> properties in the options.params property to establish the initial
13766      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13767      * <p>
13768      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13769      * and this call will return before the new data has been loaded. Perform any post-processing
13770      * in a callback function, or in a "load" event handler.</strong>
13771      * <p>
13772      * @param {Object} options An object containing properties which control loading options:<ul>
13773      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13774      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13775      * passed the following arguments:<ul>
13776      * <li>r : Roo.data.Record[]</li>
13777      * <li>options: Options object from the load call</li>
13778      * <li>success: Boolean success indicator</li></ul></li>
13779      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13780      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13781      * </ul>
13782      */
13783     load : function(options){
13784         options = options || {};
13785         if(this.fireEvent("beforeload", this, options) !== false){
13786             this.storeOptions(options);
13787             var p = Roo.apply(options.params || {}, this.baseParams);
13788             // if meta was not loaded from remote source.. try requesting it.
13789             if (!this.reader.metaFromRemote) {
13790                 p._requestMeta = 1;
13791             }
13792             if(this.sortInfo && this.remoteSort){
13793                 var pn = this.paramNames;
13794                 p[pn["sort"]] = this.sortInfo.field;
13795                 p[pn["dir"]] = this.sortInfo.direction;
13796             }
13797             if (this.multiSort) {
13798                 var pn = this.paramNames;
13799                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13800             }
13801             
13802             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13803         }
13804     },
13805
13806     /**
13807      * Reloads the Record cache from the configured Proxy using the configured Reader and
13808      * the options from the last load operation performed.
13809      * @param {Object} options (optional) An object containing properties which may override the options
13810      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13811      * the most recently used options are reused).
13812      */
13813     reload : function(options){
13814         this.load(Roo.applyIf(options||{}, this.lastOptions));
13815     },
13816
13817     // private
13818     // Called as a callback by the Reader during a load operation.
13819     loadRecords : function(o, options, success){
13820         if(!o || success === false){
13821             if(success !== false){
13822                 this.fireEvent("load", this, [], options, o);
13823             }
13824             if(options.callback){
13825                 options.callback.call(options.scope || this, [], options, false);
13826             }
13827             return;
13828         }
13829         // if data returned failure - throw an exception.
13830         if (o.success === false) {
13831             // show a message if no listener is registered.
13832             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13833                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13834             }
13835             // loadmask wil be hooked into this..
13836             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13837             return;
13838         }
13839         var r = o.records, t = o.totalRecords || r.length;
13840         
13841         this.fireEvent("beforeloadadd", this, r, options, o);
13842         
13843         if(!options || options.add !== true){
13844             if(this.pruneModifiedRecords){
13845                 this.modified = [];
13846             }
13847             for(var i = 0, len = r.length; i < len; i++){
13848                 r[i].join(this);
13849             }
13850             if(this.snapshot){
13851                 this.data = this.snapshot;
13852                 delete this.snapshot;
13853             }
13854             this.data.clear();
13855             this.data.addAll(r);
13856             this.totalLength = t;
13857             this.applySort();
13858             this.fireEvent("datachanged", this);
13859         }else{
13860             this.totalLength = Math.max(t, this.data.length+r.length);
13861             this.add(r);
13862         }
13863         
13864         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13865                 
13866             var e = new Roo.data.Record({});
13867
13868             e.set(this.parent.displayField, this.parent.emptyTitle);
13869             e.set(this.parent.valueField, '');
13870
13871             this.insert(0, e);
13872         }
13873             
13874         this.fireEvent("load", this, r, options, o);
13875         if(options.callback){
13876             options.callback.call(options.scope || this, r, options, true);
13877         }
13878     },
13879
13880
13881     /**
13882      * Loads data from a passed data block. A Reader which understands the format of the data
13883      * must have been configured in the constructor.
13884      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13885      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13886      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13887      */
13888     loadData : function(o, append){
13889         var r = this.reader.readRecords(o);
13890         this.loadRecords(r, {add: append}, true);
13891     },
13892     
13893      /**
13894      * using 'cn' the nested child reader read the child array into it's child stores.
13895      * @param {Object} rec The record with a 'children array
13896      */
13897     loadDataFromChildren : function(rec)
13898     {
13899         this.loadData(this.reader.toLoadData(rec));
13900     },
13901     
13902
13903     /**
13904      * Gets the number of cached records.
13905      * <p>
13906      * <em>If using paging, this may not be the total size of the dataset. If the data object
13907      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13908      * the data set size</em>
13909      */
13910     getCount : function(){
13911         return this.data.length || 0;
13912     },
13913
13914     /**
13915      * Gets the total number of records in the dataset as returned by the server.
13916      * <p>
13917      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13918      * the dataset size</em>
13919      */
13920     getTotalCount : function(){
13921         return this.totalLength || 0;
13922     },
13923
13924     /**
13925      * Returns the sort state of the Store as an object with two properties:
13926      * <pre><code>
13927  field {String} The name of the field by which the Records are sorted
13928  direction {String} The sort order, "ASC" or "DESC"
13929      * </code></pre>
13930      */
13931     getSortState : function(){
13932         return this.sortInfo;
13933     },
13934
13935     // private
13936     applySort : function(){
13937         if(this.sortInfo && !this.remoteSort){
13938             var s = this.sortInfo, f = s.field;
13939             var st = this.fields.get(f).sortType;
13940             var fn = function(r1, r2){
13941                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13942                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13943             };
13944             this.data.sort(s.direction, fn);
13945             if(this.snapshot && this.snapshot != this.data){
13946                 this.snapshot.sort(s.direction, fn);
13947             }
13948         }
13949     },
13950
13951     /**
13952      * Sets the default sort column and order to be used by the next load operation.
13953      * @param {String} fieldName The name of the field to sort by.
13954      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13955      */
13956     setDefaultSort : function(field, dir){
13957         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13958     },
13959
13960     /**
13961      * Sort the Records.
13962      * If remote sorting is used, the sort is performed on the server, and the cache is
13963      * reloaded. If local sorting is used, the cache is sorted internally.
13964      * @param {String} fieldName The name of the field to sort by.
13965      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13966      */
13967     sort : function(fieldName, dir){
13968         var f = this.fields.get(fieldName);
13969         if(!dir){
13970             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13971             
13972             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13973                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13974             }else{
13975                 dir = f.sortDir;
13976             }
13977         }
13978         this.sortToggle[f.name] = dir;
13979         this.sortInfo = {field: f.name, direction: dir};
13980         if(!this.remoteSort){
13981             this.applySort();
13982             this.fireEvent("datachanged", this);
13983         }else{
13984             this.load(this.lastOptions);
13985         }
13986     },
13987
13988     /**
13989      * Calls the specified function for each of the Records in the cache.
13990      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13991      * Returning <em>false</em> aborts and exits the iteration.
13992      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13993      */
13994     each : function(fn, scope){
13995         this.data.each(fn, scope);
13996     },
13997
13998     /**
13999      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14000      * (e.g., during paging).
14001      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14002      */
14003     getModifiedRecords : function(){
14004         return this.modified;
14005     },
14006
14007     // private
14008     createFilterFn : function(property, value, anyMatch){
14009         if(!value.exec){ // not a regex
14010             value = String(value);
14011             if(value.length == 0){
14012                 return false;
14013             }
14014             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14015         }
14016         return function(r){
14017             return value.test(r.data[property]);
14018         };
14019     },
14020
14021     /**
14022      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14023      * @param {String} property A field on your records
14024      * @param {Number} start The record index to start at (defaults to 0)
14025      * @param {Number} end The last record index to include (defaults to length - 1)
14026      * @return {Number} The sum
14027      */
14028     sum : function(property, start, end){
14029         var rs = this.data.items, v = 0;
14030         start = start || 0;
14031         end = (end || end === 0) ? end : rs.length-1;
14032
14033         for(var i = start; i <= end; i++){
14034             v += (rs[i].data[property] || 0);
14035         }
14036         return v;
14037     },
14038
14039     /**
14040      * Filter the records by a specified property.
14041      * @param {String} field A field on your records
14042      * @param {String/RegExp} value Either a string that the field
14043      * should start with or a RegExp to test against the field
14044      * @param {Boolean} anyMatch True to match any part not just the beginning
14045      */
14046     filter : function(property, value, anyMatch){
14047         var fn = this.createFilterFn(property, value, anyMatch);
14048         return fn ? this.filterBy(fn) : this.clearFilter();
14049     },
14050
14051     /**
14052      * Filter by a function. The specified function will be called with each
14053      * record in this data source. If the function returns true the record is included,
14054      * otherwise it is filtered.
14055      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14056      * @param {Object} scope (optional) The scope of the function (defaults to this)
14057      */
14058     filterBy : function(fn, scope){
14059         this.snapshot = this.snapshot || this.data;
14060         this.data = this.queryBy(fn, scope||this);
14061         this.fireEvent("datachanged", this);
14062     },
14063
14064     /**
14065      * Query the records by a specified property.
14066      * @param {String} field A field on your records
14067      * @param {String/RegExp} value Either a string that the field
14068      * should start with or a RegExp to test against the field
14069      * @param {Boolean} anyMatch True to match any part not just the beginning
14070      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14071      */
14072     query : function(property, value, anyMatch){
14073         var fn = this.createFilterFn(property, value, anyMatch);
14074         return fn ? this.queryBy(fn) : this.data.clone();
14075     },
14076
14077     /**
14078      * Query by a function. The specified function will be called with each
14079      * record in this data source. If the function returns true the record is included
14080      * in the results.
14081      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14082      * @param {Object} scope (optional) The scope of the function (defaults to this)
14083       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14084      **/
14085     queryBy : function(fn, scope){
14086         var data = this.snapshot || this.data;
14087         return data.filterBy(fn, scope||this);
14088     },
14089
14090     /**
14091      * Collects unique values for a particular dataIndex from this store.
14092      * @param {String} dataIndex The property to collect
14093      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14094      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14095      * @return {Array} An array of the unique values
14096      **/
14097     collect : function(dataIndex, allowNull, bypassFilter){
14098         var d = (bypassFilter === true && this.snapshot) ?
14099                 this.snapshot.items : this.data.items;
14100         var v, sv, r = [], l = {};
14101         for(var i = 0, len = d.length; i < len; i++){
14102             v = d[i].data[dataIndex];
14103             sv = String(v);
14104             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14105                 l[sv] = true;
14106                 r[r.length] = v;
14107             }
14108         }
14109         return r;
14110     },
14111
14112     /**
14113      * Revert to a view of the Record cache with no filtering applied.
14114      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14115      */
14116     clearFilter : function(suppressEvent){
14117         if(this.snapshot && this.snapshot != this.data){
14118             this.data = this.snapshot;
14119             delete this.snapshot;
14120             if(suppressEvent !== true){
14121                 this.fireEvent("datachanged", this);
14122             }
14123         }
14124     },
14125
14126     // private
14127     afterEdit : function(record){
14128         if(this.modified.indexOf(record) == -1){
14129             this.modified.push(record);
14130         }
14131         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14132     },
14133     
14134     // private
14135     afterReject : function(record){
14136         this.modified.remove(record);
14137         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14138     },
14139
14140     // private
14141     afterCommit : function(record){
14142         this.modified.remove(record);
14143         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14144     },
14145
14146     /**
14147      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14148      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14149      */
14150     commitChanges : function(){
14151         var m = this.modified.slice(0);
14152         this.modified = [];
14153         for(var i = 0, len = m.length; i < len; i++){
14154             m[i].commit();
14155         }
14156     },
14157
14158     /**
14159      * Cancel outstanding changes on all changed records.
14160      */
14161     rejectChanges : function(){
14162         var m = this.modified.slice(0);
14163         this.modified = [];
14164         for(var i = 0, len = m.length; i < len; i++){
14165             m[i].reject();
14166         }
14167     },
14168
14169     onMetaChange : function(meta, rtype, o){
14170         this.recordType = rtype;
14171         this.fields = rtype.prototype.fields;
14172         delete this.snapshot;
14173         this.sortInfo = meta.sortInfo || this.sortInfo;
14174         this.modified = [];
14175         this.fireEvent('metachange', this, this.reader.meta);
14176     },
14177     
14178     moveIndex : function(data, type)
14179     {
14180         var index = this.indexOf(data);
14181         
14182         var newIndex = index + type;
14183         
14184         this.remove(data);
14185         
14186         this.insert(newIndex, data);
14187         
14188     }
14189 });/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.data.SimpleStore
14202  * @extends Roo.data.Store
14203  * Small helper class to make creating Stores from Array data easier.
14204  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14205  * @cfg {Array} fields An array of field definition objects, or field name strings.
14206  * @cfg {Object} an existing reader (eg. copied from another store)
14207  * @cfg {Array} data The multi-dimensional array of data
14208  * @constructor
14209  * @param {Object} config
14210  */
14211 Roo.data.SimpleStore = function(config)
14212 {
14213     Roo.data.SimpleStore.superclass.constructor.call(this, {
14214         isLocal : true,
14215         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14216                 id: config.id
14217             },
14218             Roo.data.Record.create(config.fields)
14219         ),
14220         proxy : new Roo.data.MemoryProxy(config.data)
14221     });
14222     this.load();
14223 };
14224 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14225  * Based on:
14226  * Ext JS Library 1.1.1
14227  * Copyright(c) 2006-2007, Ext JS, LLC.
14228  *
14229  * Originally Released Under LGPL - original licence link has changed is not relivant.
14230  *
14231  * Fork - LGPL
14232  * <script type="text/javascript">
14233  */
14234
14235 /**
14236 /**
14237  * @extends Roo.data.Store
14238  * @class Roo.data.JsonStore
14239  * Small helper class to make creating Stores for JSON data easier. <br/>
14240 <pre><code>
14241 var store = new Roo.data.JsonStore({
14242     url: 'get-images.php',
14243     root: 'images',
14244     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14245 });
14246 </code></pre>
14247  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14248  * JsonReader and HttpProxy (unless inline data is provided).</b>
14249  * @cfg {Array} fields An array of field definition objects, or field name strings.
14250  * @constructor
14251  * @param {Object} config
14252  */
14253 Roo.data.JsonStore = function(c){
14254     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14255         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14256         reader: new Roo.data.JsonReader(c, c.fields)
14257     }));
14258 };
14259 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14260  * Based on:
14261  * Ext JS Library 1.1.1
14262  * Copyright(c) 2006-2007, Ext JS, LLC.
14263  *
14264  * Originally Released Under LGPL - original licence link has changed is not relivant.
14265  *
14266  * Fork - LGPL
14267  * <script type="text/javascript">
14268  */
14269
14270  
14271 Roo.data.Field = function(config){
14272     if(typeof config == "string"){
14273         config = {name: config};
14274     }
14275     Roo.apply(this, config);
14276     
14277     if(!this.type){
14278         this.type = "auto";
14279     }
14280     
14281     var st = Roo.data.SortTypes;
14282     // named sortTypes are supported, here we look them up
14283     if(typeof this.sortType == "string"){
14284         this.sortType = st[this.sortType];
14285     }
14286     
14287     // set default sortType for strings and dates
14288     if(!this.sortType){
14289         switch(this.type){
14290             case "string":
14291                 this.sortType = st.asUCString;
14292                 break;
14293             case "date":
14294                 this.sortType = st.asDate;
14295                 break;
14296             default:
14297                 this.sortType = st.none;
14298         }
14299     }
14300
14301     // define once
14302     var stripRe = /[\$,%]/g;
14303
14304     // prebuilt conversion function for this field, instead of
14305     // switching every time we're reading a value
14306     if(!this.convert){
14307         var cv, dateFormat = this.dateFormat;
14308         switch(this.type){
14309             case "":
14310             case "auto":
14311             case undefined:
14312                 cv = function(v){ return v; };
14313                 break;
14314             case "string":
14315                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14316                 break;
14317             case "int":
14318                 cv = function(v){
14319                     return v !== undefined && v !== null && v !== '' ?
14320                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14321                     };
14322                 break;
14323             case "float":
14324                 cv = function(v){
14325                     return v !== undefined && v !== null && v !== '' ?
14326                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14327                     };
14328                 break;
14329             case "bool":
14330             case "boolean":
14331                 cv = function(v){ return v === true || v === "true" || v == 1; };
14332                 break;
14333             case "date":
14334                 cv = function(v){
14335                     if(!v){
14336                         return '';
14337                     }
14338                     if(v instanceof Date){
14339                         return v;
14340                     }
14341                     if(dateFormat){
14342                         if(dateFormat == "timestamp"){
14343                             return new Date(v*1000);
14344                         }
14345                         return Date.parseDate(v, dateFormat);
14346                     }
14347                     var parsed = Date.parse(v);
14348                     return parsed ? new Date(parsed) : null;
14349                 };
14350              break;
14351             
14352         }
14353         this.convert = cv;
14354     }
14355 };
14356
14357 Roo.data.Field.prototype = {
14358     dateFormat: null,
14359     defaultValue: "",
14360     mapping: null,
14361     sortType : null,
14362     sortDir : "ASC"
14363 };/*
14364  * Based on:
14365  * Ext JS Library 1.1.1
14366  * Copyright(c) 2006-2007, Ext JS, LLC.
14367  *
14368  * Originally Released Under LGPL - original licence link has changed is not relivant.
14369  *
14370  * Fork - LGPL
14371  * <script type="text/javascript">
14372  */
14373  
14374 // Base class for reading structured data from a data source.  This class is intended to be
14375 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14376
14377 /**
14378  * @class Roo.data.DataReader
14379  * Base class for reading structured data from a data source.  This class is intended to be
14380  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14381  */
14382
14383 Roo.data.DataReader = function(meta, recordType){
14384     
14385     this.meta = meta;
14386     
14387     this.recordType = recordType instanceof Array ? 
14388         Roo.data.Record.create(recordType) : recordType;
14389 };
14390
14391 Roo.data.DataReader.prototype = {
14392     
14393     
14394     readerType : 'Data',
14395      /**
14396      * Create an empty record
14397      * @param {Object} data (optional) - overlay some values
14398      * @return {Roo.data.Record} record created.
14399      */
14400     newRow :  function(d) {
14401         var da =  {};
14402         this.recordType.prototype.fields.each(function(c) {
14403             switch( c.type) {
14404                 case 'int' : da[c.name] = 0; break;
14405                 case 'date' : da[c.name] = new Date(); break;
14406                 case 'float' : da[c.name] = 0.0; break;
14407                 case 'boolean' : da[c.name] = false; break;
14408                 default : da[c.name] = ""; break;
14409             }
14410             
14411         });
14412         return new this.recordType(Roo.apply(da, d));
14413     }
14414     
14415     
14416 };/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426
14427 /**
14428  * @class Roo.data.DataProxy
14429  * @extends Roo.data.Observable
14430  * This class is an abstract base class for implementations which provide retrieval of
14431  * unformatted data objects.<br>
14432  * <p>
14433  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14434  * (of the appropriate type which knows how to parse the data object) to provide a block of
14435  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14436  * <p>
14437  * Custom implementations must implement the load method as described in
14438  * {@link Roo.data.HttpProxy#load}.
14439  */
14440 Roo.data.DataProxy = function(){
14441     this.addEvents({
14442         /**
14443          * @event beforeload
14444          * Fires before a network request is made to retrieve a data object.
14445          * @param {Object} This DataProxy object.
14446          * @param {Object} params The params parameter to the load function.
14447          */
14448         beforeload : true,
14449         /**
14450          * @event load
14451          * Fires before the load method's callback is called.
14452          * @param {Object} This DataProxy object.
14453          * @param {Object} o The data object.
14454          * @param {Object} arg The callback argument object passed to the load function.
14455          */
14456         load : true,
14457         /**
14458          * @event loadexception
14459          * Fires if an Exception occurs during data retrieval.
14460          * @param {Object} This DataProxy object.
14461          * @param {Object} o The data object.
14462          * @param {Object} arg The callback argument object passed to the load function.
14463          * @param {Object} e The Exception.
14464          */
14465         loadexception : true
14466     });
14467     Roo.data.DataProxy.superclass.constructor.call(this);
14468 };
14469
14470 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14471
14472     /**
14473      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14474      */
14475 /*
14476  * Based on:
14477  * Ext JS Library 1.1.1
14478  * Copyright(c) 2006-2007, Ext JS, LLC.
14479  *
14480  * Originally Released Under LGPL - original licence link has changed is not relivant.
14481  *
14482  * Fork - LGPL
14483  * <script type="text/javascript">
14484  */
14485 /**
14486  * @class Roo.data.MemoryProxy
14487  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14488  * to the Reader when its load method is called.
14489  * @constructor
14490  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14491  */
14492 Roo.data.MemoryProxy = function(data){
14493     if (data.data) {
14494         data = data.data;
14495     }
14496     Roo.data.MemoryProxy.superclass.constructor.call(this);
14497     this.data = data;
14498 };
14499
14500 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14501     
14502     /**
14503      * Load data from the requested source (in this case an in-memory
14504      * data object passed to the constructor), read the data object into
14505      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14506      * process that block using the passed callback.
14507      * @param {Object} params This parameter is not used by the MemoryProxy class.
14508      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14509      * object into a block of Roo.data.Records.
14510      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14511      * The function must be passed <ul>
14512      * <li>The Record block object</li>
14513      * <li>The "arg" argument from the load function</li>
14514      * <li>A boolean success indicator</li>
14515      * </ul>
14516      * @param {Object} scope The scope in which to call the callback
14517      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14518      */
14519     load : function(params, reader, callback, scope, arg){
14520         params = params || {};
14521         var result;
14522         try {
14523             result = reader.readRecords(params.data ? params.data :this.data);
14524         }catch(e){
14525             this.fireEvent("loadexception", this, arg, null, e);
14526             callback.call(scope, null, arg, false);
14527             return;
14528         }
14529         callback.call(scope, result, arg, true);
14530     },
14531     
14532     // private
14533     update : function(params, records){
14534         
14535     }
14536 });/*
14537  * Based on:
14538  * Ext JS Library 1.1.1
14539  * Copyright(c) 2006-2007, Ext JS, LLC.
14540  *
14541  * Originally Released Under LGPL - original licence link has changed is not relivant.
14542  *
14543  * Fork - LGPL
14544  * <script type="text/javascript">
14545  */
14546 /**
14547  * @class Roo.data.HttpProxy
14548  * @extends Roo.data.DataProxy
14549  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14550  * configured to reference a certain URL.<br><br>
14551  * <p>
14552  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14553  * from which the running page was served.<br><br>
14554  * <p>
14555  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14556  * <p>
14557  * Be aware that to enable the browser to parse an XML document, the server must set
14558  * the Content-Type header in the HTTP response to "text/xml".
14559  * @constructor
14560  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14561  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14562  * will be used to make the request.
14563  */
14564 Roo.data.HttpProxy = function(conn){
14565     Roo.data.HttpProxy.superclass.constructor.call(this);
14566     // is conn a conn config or a real conn?
14567     this.conn = conn;
14568     this.useAjax = !conn || !conn.events;
14569   
14570 };
14571
14572 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14573     // thse are take from connection...
14574     
14575     /**
14576      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14577      */
14578     /**
14579      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14580      * extra parameters to each request made by this object. (defaults to undefined)
14581      */
14582     /**
14583      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14584      *  to each request made by this object. (defaults to undefined)
14585      */
14586     /**
14587      * @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)
14588      */
14589     /**
14590      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14591      */
14592      /**
14593      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14594      * @type Boolean
14595      */
14596   
14597
14598     /**
14599      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14600      * @type Boolean
14601      */
14602     /**
14603      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14604      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14605      * a finer-grained basis than the DataProxy events.
14606      */
14607     getConnection : function(){
14608         return this.useAjax ? Roo.Ajax : this.conn;
14609     },
14610
14611     /**
14612      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14613      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14614      * process that block using the passed callback.
14615      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14616      * for the request to the remote server.
14617      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14618      * object into a block of Roo.data.Records.
14619      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14620      * The function must be passed <ul>
14621      * <li>The Record block object</li>
14622      * <li>The "arg" argument from the load function</li>
14623      * <li>A boolean success indicator</li>
14624      * </ul>
14625      * @param {Object} scope The scope in which to call the callback
14626      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14627      */
14628     load : function(params, reader, callback, scope, arg){
14629         if(this.fireEvent("beforeload", this, params) !== false){
14630             var  o = {
14631                 params : params || {},
14632                 request: {
14633                     callback : callback,
14634                     scope : scope,
14635                     arg : arg
14636                 },
14637                 reader: reader,
14638                 callback : this.loadResponse,
14639                 scope: this
14640             };
14641             if(this.useAjax){
14642                 Roo.applyIf(o, this.conn);
14643                 if(this.activeRequest){
14644                     Roo.Ajax.abort(this.activeRequest);
14645                 }
14646                 this.activeRequest = Roo.Ajax.request(o);
14647             }else{
14648                 this.conn.request(o);
14649             }
14650         }else{
14651             callback.call(scope||this, null, arg, false);
14652         }
14653     },
14654
14655     // private
14656     loadResponse : function(o, success, response){
14657         delete this.activeRequest;
14658         if(!success){
14659             this.fireEvent("loadexception", this, o, response);
14660             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14661             return;
14662         }
14663         var result;
14664         try {
14665             result = o.reader.read(response);
14666         }catch(e){
14667             this.fireEvent("loadexception", this, o, response, e);
14668             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14669             return;
14670         }
14671         
14672         this.fireEvent("load", this, o, o.request.arg);
14673         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14674     },
14675
14676     // private
14677     update : function(dataSet){
14678
14679     },
14680
14681     // private
14682     updateResponse : function(dataSet){
14683
14684     }
14685 });/*
14686  * Based on:
14687  * Ext JS Library 1.1.1
14688  * Copyright(c) 2006-2007, Ext JS, LLC.
14689  *
14690  * Originally Released Under LGPL - original licence link has changed is not relivant.
14691  *
14692  * Fork - LGPL
14693  * <script type="text/javascript">
14694  */
14695
14696 /**
14697  * @class Roo.data.ScriptTagProxy
14698  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14699  * other than the originating domain of the running page.<br><br>
14700  * <p>
14701  * <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
14702  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14703  * <p>
14704  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14705  * source code that is used as the source inside a &lt;script> tag.<br><br>
14706  * <p>
14707  * In order for the browser to process the returned data, the server must wrap the data object
14708  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14709  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14710  * depending on whether the callback name was passed:
14711  * <p>
14712  * <pre><code>
14713 boolean scriptTag = false;
14714 String cb = request.getParameter("callback");
14715 if (cb != null) {
14716     scriptTag = true;
14717     response.setContentType("text/javascript");
14718 } else {
14719     response.setContentType("application/x-json");
14720 }
14721 Writer out = response.getWriter();
14722 if (scriptTag) {
14723     out.write(cb + "(");
14724 }
14725 out.print(dataBlock.toJsonString());
14726 if (scriptTag) {
14727     out.write(");");
14728 }
14729 </pre></code>
14730  *
14731  * @constructor
14732  * @param {Object} config A configuration object.
14733  */
14734 Roo.data.ScriptTagProxy = function(config){
14735     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14736     Roo.apply(this, config);
14737     this.head = document.getElementsByTagName("head")[0];
14738 };
14739
14740 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14741
14742 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14743     /**
14744      * @cfg {String} url The URL from which to request the data object.
14745      */
14746     /**
14747      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14748      */
14749     timeout : 30000,
14750     /**
14751      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14752      * the server the name of the callback function set up by the load call to process the returned data object.
14753      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14754      * javascript output which calls this named function passing the data object as its only parameter.
14755      */
14756     callbackParam : "callback",
14757     /**
14758      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14759      * name to the request.
14760      */
14761     nocache : true,
14762
14763     /**
14764      * Load data from the configured URL, read the data object into
14765      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14766      * process that block using the passed callback.
14767      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14768      * for the request to the remote server.
14769      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14770      * object into a block of Roo.data.Records.
14771      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14772      * The function must be passed <ul>
14773      * <li>The Record block object</li>
14774      * <li>The "arg" argument from the load function</li>
14775      * <li>A boolean success indicator</li>
14776      * </ul>
14777      * @param {Object} scope The scope in which to call the callback
14778      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14779      */
14780     load : function(params, reader, callback, scope, arg){
14781         if(this.fireEvent("beforeload", this, params) !== false){
14782
14783             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14784
14785             var url = this.url;
14786             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14787             if(this.nocache){
14788                 url += "&_dc=" + (new Date().getTime());
14789             }
14790             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14791             var trans = {
14792                 id : transId,
14793                 cb : "stcCallback"+transId,
14794                 scriptId : "stcScript"+transId,
14795                 params : params,
14796                 arg : arg,
14797                 url : url,
14798                 callback : callback,
14799                 scope : scope,
14800                 reader : reader
14801             };
14802             var conn = this;
14803
14804             window[trans.cb] = function(o){
14805                 conn.handleResponse(o, trans);
14806             };
14807
14808             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14809
14810             if(this.autoAbort !== false){
14811                 this.abort();
14812             }
14813
14814             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14815
14816             var script = document.createElement("script");
14817             script.setAttribute("src", url);
14818             script.setAttribute("type", "text/javascript");
14819             script.setAttribute("id", trans.scriptId);
14820             this.head.appendChild(script);
14821
14822             this.trans = trans;
14823         }else{
14824             callback.call(scope||this, null, arg, false);
14825         }
14826     },
14827
14828     // private
14829     isLoading : function(){
14830         return this.trans ? true : false;
14831     },
14832
14833     /**
14834      * Abort the current server request.
14835      */
14836     abort : function(){
14837         if(this.isLoading()){
14838             this.destroyTrans(this.trans);
14839         }
14840     },
14841
14842     // private
14843     destroyTrans : function(trans, isLoaded){
14844         this.head.removeChild(document.getElementById(trans.scriptId));
14845         clearTimeout(trans.timeoutId);
14846         if(isLoaded){
14847             window[trans.cb] = undefined;
14848             try{
14849                 delete window[trans.cb];
14850             }catch(e){}
14851         }else{
14852             // if hasn't been loaded, wait for load to remove it to prevent script error
14853             window[trans.cb] = function(){
14854                 window[trans.cb] = undefined;
14855                 try{
14856                     delete window[trans.cb];
14857                 }catch(e){}
14858             };
14859         }
14860     },
14861
14862     // private
14863     handleResponse : function(o, trans){
14864         this.trans = false;
14865         this.destroyTrans(trans, true);
14866         var result;
14867         try {
14868             result = trans.reader.readRecords(o);
14869         }catch(e){
14870             this.fireEvent("loadexception", this, o, trans.arg, e);
14871             trans.callback.call(trans.scope||window, null, trans.arg, false);
14872             return;
14873         }
14874         this.fireEvent("load", this, o, trans.arg);
14875         trans.callback.call(trans.scope||window, result, trans.arg, true);
14876     },
14877
14878     // private
14879     handleFailure : function(trans){
14880         this.trans = false;
14881         this.destroyTrans(trans, false);
14882         this.fireEvent("loadexception", this, null, trans.arg);
14883         trans.callback.call(trans.scope||window, null, trans.arg, false);
14884     }
14885 });/*
14886  * Based on:
14887  * Ext JS Library 1.1.1
14888  * Copyright(c) 2006-2007, Ext JS, LLC.
14889  *
14890  * Originally Released Under LGPL - original licence link has changed is not relivant.
14891  *
14892  * Fork - LGPL
14893  * <script type="text/javascript">
14894  */
14895
14896 /**
14897  * @class Roo.data.JsonReader
14898  * @extends Roo.data.DataReader
14899  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14900  * based on mappings in a provided Roo.data.Record constructor.
14901  * 
14902  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14903  * in the reply previously. 
14904  * 
14905  * <p>
14906  * Example code:
14907  * <pre><code>
14908 var RecordDef = Roo.data.Record.create([
14909     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14910     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14911 ]);
14912 var myReader = new Roo.data.JsonReader({
14913     totalProperty: "results",    // The property which contains the total dataset size (optional)
14914     root: "rows",                // The property which contains an Array of row objects
14915     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14916 }, RecordDef);
14917 </code></pre>
14918  * <p>
14919  * This would consume a JSON file like this:
14920  * <pre><code>
14921 { 'results': 2, 'rows': [
14922     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14923     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14924 }
14925 </code></pre>
14926  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14927  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14928  * paged from the remote server.
14929  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14930  * @cfg {String} root name of the property which contains the Array of row objects.
14931  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14932  * @cfg {Array} fields Array of field definition objects
14933  * @constructor
14934  * Create a new JsonReader
14935  * @param {Object} meta Metadata configuration options
14936  * @param {Object} recordType Either an Array of field definition objects,
14937  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14938  */
14939 Roo.data.JsonReader = function(meta, recordType){
14940     
14941     meta = meta || {};
14942     // set some defaults:
14943     Roo.applyIf(meta, {
14944         totalProperty: 'total',
14945         successProperty : 'success',
14946         root : 'data',
14947         id : 'id'
14948     });
14949     
14950     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14951 };
14952 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14953     
14954     readerType : 'Json',
14955     
14956     /**
14957      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14958      * Used by Store query builder to append _requestMeta to params.
14959      * 
14960      */
14961     metaFromRemote : false,
14962     /**
14963      * This method is only used by a DataProxy which has retrieved data from a remote server.
14964      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14965      * @return {Object} data A data block which is used by an Roo.data.Store object as
14966      * a cache of Roo.data.Records.
14967      */
14968     read : function(response){
14969         var json = response.responseText;
14970        
14971         var o = /* eval:var:o */ eval("("+json+")");
14972         if(!o) {
14973             throw {message: "JsonReader.read: Json object not found"};
14974         }
14975         
14976         if(o.metaData){
14977             
14978             delete this.ef;
14979             this.metaFromRemote = true;
14980             this.meta = o.metaData;
14981             this.recordType = Roo.data.Record.create(o.metaData.fields);
14982             this.onMetaChange(this.meta, this.recordType, o);
14983         }
14984         return this.readRecords(o);
14985     },
14986
14987     // private function a store will implement
14988     onMetaChange : function(meta, recordType, o){
14989
14990     },
14991
14992     /**
14993          * @ignore
14994          */
14995     simpleAccess: function(obj, subsc) {
14996         return obj[subsc];
14997     },
14998
14999         /**
15000          * @ignore
15001          */
15002     getJsonAccessor: function(){
15003         var re = /[\[\.]/;
15004         return function(expr) {
15005             try {
15006                 return(re.test(expr))
15007                     ? new Function("obj", "return obj." + expr)
15008                     : function(obj){
15009                         return obj[expr];
15010                     };
15011             } catch(e){}
15012             return Roo.emptyFn;
15013         };
15014     }(),
15015
15016     /**
15017      * Create a data block containing Roo.data.Records from an XML document.
15018      * @param {Object} o An object which contains an Array of row objects in the property specified
15019      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15020      * which contains the total size of the dataset.
15021      * @return {Object} data A data block which is used by an Roo.data.Store object as
15022      * a cache of Roo.data.Records.
15023      */
15024     readRecords : function(o){
15025         /**
15026          * After any data loads, the raw JSON data is available for further custom processing.
15027          * @type Object
15028          */
15029         this.o = o;
15030         var s = this.meta, Record = this.recordType,
15031             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15032
15033 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15034         if (!this.ef) {
15035             if(s.totalProperty) {
15036                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15037                 }
15038                 if(s.successProperty) {
15039                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15040                 }
15041                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15042                 if (s.id) {
15043                         var g = this.getJsonAccessor(s.id);
15044                         this.getId = function(rec) {
15045                                 var r = g(rec);  
15046                                 return (r === undefined || r === "") ? null : r;
15047                         };
15048                 } else {
15049                         this.getId = function(){return null;};
15050                 }
15051             this.ef = [];
15052             for(var jj = 0; jj < fl; jj++){
15053                 f = fi[jj];
15054                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15055                 this.ef[jj] = this.getJsonAccessor(map);
15056             }
15057         }
15058
15059         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15060         if(s.totalProperty){
15061             var vt = parseInt(this.getTotal(o), 10);
15062             if(!isNaN(vt)){
15063                 totalRecords = vt;
15064             }
15065         }
15066         if(s.successProperty){
15067             var vs = this.getSuccess(o);
15068             if(vs === false || vs === 'false'){
15069                 success = false;
15070             }
15071         }
15072         var records = [];
15073         for(var i = 0; i < c; i++){
15074                 var n = root[i];
15075             var values = {};
15076             var id = this.getId(n);
15077             for(var j = 0; j < fl; j++){
15078                 f = fi[j];
15079             var v = this.ef[j](n);
15080             if (!f.convert) {
15081                 Roo.log('missing convert for ' + f.name);
15082                 Roo.log(f);
15083                 continue;
15084             }
15085             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15086             }
15087             var record = new Record(values, id);
15088             record.json = n;
15089             records[i] = record;
15090         }
15091         return {
15092             raw : o,
15093             success : success,
15094             records : records,
15095             totalRecords : totalRecords
15096         };
15097     },
15098     // used when loading children.. @see loadDataFromChildren
15099     toLoadData: function(rec)
15100     {
15101         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15102         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15103         return { data : data, total : data.length };
15104         
15105     }
15106 });/*
15107  * Based on:
15108  * Ext JS Library 1.1.1
15109  * Copyright(c) 2006-2007, Ext JS, LLC.
15110  *
15111  * Originally Released Under LGPL - original licence link has changed is not relivant.
15112  *
15113  * Fork - LGPL
15114  * <script type="text/javascript">
15115  */
15116
15117 /**
15118  * @class Roo.data.ArrayReader
15119  * @extends Roo.data.DataReader
15120  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15121  * Each element of that Array represents a row of data fields. The
15122  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15123  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15124  * <p>
15125  * Example code:.
15126  * <pre><code>
15127 var RecordDef = Roo.data.Record.create([
15128     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15129     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15130 ]);
15131 var myReader = new Roo.data.ArrayReader({
15132     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15133 }, RecordDef);
15134 </code></pre>
15135  * <p>
15136  * This would consume an Array like this:
15137  * <pre><code>
15138 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15139   </code></pre>
15140  
15141  * @constructor
15142  * Create a new JsonReader
15143  * @param {Object} meta Metadata configuration options.
15144  * @param {Object|Array} recordType Either an Array of field definition objects
15145  * 
15146  * @cfg {Array} fields Array of field definition objects
15147  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15148  * as specified to {@link Roo.data.Record#create},
15149  * or an {@link Roo.data.Record} object
15150  *
15151  * 
15152  * created using {@link Roo.data.Record#create}.
15153  */
15154 Roo.data.ArrayReader = function(meta, recordType)
15155 {    
15156     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15157 };
15158
15159 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15160     
15161       /**
15162      * Create a data block containing Roo.data.Records from an XML document.
15163      * @param {Object} o An Array of row objects which represents the dataset.
15164      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15165      * a cache of Roo.data.Records.
15166      */
15167     readRecords : function(o)
15168     {
15169         var sid = this.meta ? this.meta.id : null;
15170         var recordType = this.recordType, fields = recordType.prototype.fields;
15171         var records = [];
15172         var root = o;
15173         for(var i = 0; i < root.length; i++){
15174             var n = root[i];
15175             var values = {};
15176             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15177             for(var j = 0, jlen = fields.length; j < jlen; j++){
15178                 var f = fields.items[j];
15179                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15180                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15181                 v = f.convert(v);
15182                 values[f.name] = v;
15183             }
15184             var record = new recordType(values, id);
15185             record.json = n;
15186             records[records.length] = record;
15187         }
15188         return {
15189             records : records,
15190             totalRecords : records.length
15191         };
15192     },
15193     // used when loading children.. @see loadDataFromChildren
15194     toLoadData: function(rec)
15195     {
15196         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15197         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15198         
15199     }
15200     
15201     
15202 });/*
15203  * - LGPL
15204  * * 
15205  */
15206
15207 /**
15208  * @class Roo.bootstrap.ComboBox
15209  * @extends Roo.bootstrap.TriggerField
15210  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15211  * @cfg {Boolean} append (true|false) default false
15212  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15213  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15214  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15215  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15216  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15217  * @cfg {Boolean} animate default true
15218  * @cfg {Boolean} emptyResultText only for touch device
15219  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15220  * @cfg {String} emptyTitle default ''
15221  * @cfg {Number} width fixed with? experimental
15222  * @constructor
15223  * Create a new ComboBox.
15224  * @param {Object} config Configuration options
15225  */
15226 Roo.bootstrap.ComboBox = function(config){
15227     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15228     this.addEvents({
15229         /**
15230          * @event expand
15231          * Fires when the dropdown list is expanded
15232         * @param {Roo.bootstrap.ComboBox} combo This combo box
15233         */
15234         'expand' : true,
15235         /**
15236          * @event collapse
15237          * Fires when the dropdown list is collapsed
15238         * @param {Roo.bootstrap.ComboBox} combo This combo box
15239         */
15240         'collapse' : true,
15241         /**
15242          * @event beforeselect
15243          * Fires before a list item is selected. Return false to cancel the selection.
15244         * @param {Roo.bootstrap.ComboBox} combo This combo box
15245         * @param {Roo.data.Record} record The data record returned from the underlying store
15246         * @param {Number} index The index of the selected item in the dropdown list
15247         */
15248         'beforeselect' : true,
15249         /**
15250          * @event select
15251          * Fires when a list item is selected
15252         * @param {Roo.bootstrap.ComboBox} combo This combo box
15253         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15254         * @param {Number} index The index of the selected item in the dropdown list
15255         */
15256         'select' : true,
15257         /**
15258          * @event beforequery
15259          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15260          * The event object passed has these properties:
15261         * @param {Roo.bootstrap.ComboBox} combo This combo box
15262         * @param {String} query The query
15263         * @param {Boolean} forceAll true to force "all" query
15264         * @param {Boolean} cancel true to cancel the query
15265         * @param {Object} e The query event object
15266         */
15267         'beforequery': true,
15268          /**
15269          * @event add
15270          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15271         * @param {Roo.bootstrap.ComboBox} combo This combo box
15272         */
15273         'add' : true,
15274         /**
15275          * @event edit
15276          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15277         * @param {Roo.bootstrap.ComboBox} combo This combo box
15278         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15279         */
15280         'edit' : true,
15281         /**
15282          * @event remove
15283          * Fires when the remove value from the combobox array
15284         * @param {Roo.bootstrap.ComboBox} combo This combo box
15285         */
15286         'remove' : true,
15287         /**
15288          * @event afterremove
15289          * Fires when the remove value from the combobox array
15290         * @param {Roo.bootstrap.ComboBox} combo This combo box
15291         */
15292         'afterremove' : true,
15293         /**
15294          * @event specialfilter
15295          * Fires when specialfilter
15296             * @param {Roo.bootstrap.ComboBox} combo This combo box
15297             */
15298         'specialfilter' : true,
15299         /**
15300          * @event tick
15301          * Fires when tick the element
15302             * @param {Roo.bootstrap.ComboBox} combo This combo box
15303             */
15304         'tick' : true,
15305         /**
15306          * @event touchviewdisplay
15307          * Fires when touch view require special display (default is using displayField)
15308             * @param {Roo.bootstrap.ComboBox} combo This combo box
15309             * @param {Object} cfg set html .
15310             */
15311         'touchviewdisplay' : true
15312         
15313     });
15314     
15315     this.item = [];
15316     this.tickItems = [];
15317     
15318     this.selectedIndex = -1;
15319     if(this.mode == 'local'){
15320         if(config.queryDelay === undefined){
15321             this.queryDelay = 10;
15322         }
15323         if(config.minChars === undefined){
15324             this.minChars = 0;
15325         }
15326     }
15327 };
15328
15329 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15330      
15331     /**
15332      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15333      * rendering into an Roo.Editor, defaults to false)
15334      */
15335     /**
15336      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15337      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15338      */
15339     /**
15340      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15341      */
15342     /**
15343      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15344      * the dropdown list (defaults to undefined, with no header element)
15345      */
15346
15347      /**
15348      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15349      */
15350      
15351      /**
15352      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15353      */
15354     listWidth: undefined,
15355     /**
15356      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15357      * mode = 'remote' or 'text' if mode = 'local')
15358      */
15359     displayField: undefined,
15360     
15361     /**
15362      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15363      * mode = 'remote' or 'value' if mode = 'local'). 
15364      * Note: use of a valueField requires the user make a selection
15365      * in order for a value to be mapped.
15366      */
15367     valueField: undefined,
15368     /**
15369      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15370      */
15371     modalTitle : '',
15372     
15373     /**
15374      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15375      * field's data value (defaults to the underlying DOM element's name)
15376      */
15377     hiddenName: undefined,
15378     /**
15379      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15380      */
15381     listClass: '',
15382     /**
15383      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15384      */
15385     selectedClass: 'active',
15386     
15387     /**
15388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15389      */
15390     shadow:'sides',
15391     /**
15392      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15393      * anchor positions (defaults to 'tl-bl')
15394      */
15395     listAlign: 'tl-bl?',
15396     /**
15397      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15398      */
15399     maxHeight: 300,
15400     /**
15401      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15402      * query specified by the allQuery config option (defaults to 'query')
15403      */
15404     triggerAction: 'query',
15405     /**
15406      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15407      * (defaults to 4, does not apply if editable = false)
15408      */
15409     minChars : 4,
15410     /**
15411      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15412      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15413      */
15414     typeAhead: false,
15415     /**
15416      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15417      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15418      */
15419     queryDelay: 500,
15420     /**
15421      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15422      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15423      */
15424     pageSize: 0,
15425     /**
15426      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15427      * when editable = true (defaults to false)
15428      */
15429     selectOnFocus:false,
15430     /**
15431      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15432      */
15433     queryParam: 'query',
15434     /**
15435      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15436      * when mode = 'remote' (defaults to 'Loading...')
15437      */
15438     loadingText: 'Loading...',
15439     /**
15440      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15441      */
15442     resizable: false,
15443     /**
15444      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15445      */
15446     handleHeight : 8,
15447     /**
15448      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15449      * traditional select (defaults to true)
15450      */
15451     editable: true,
15452     /**
15453      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15454      */
15455     allQuery: '',
15456     /**
15457      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15458      */
15459     mode: 'remote',
15460     /**
15461      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15462      * listWidth has a higher value)
15463      */
15464     minListWidth : 70,
15465     /**
15466      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15467      * allow the user to set arbitrary text into the field (defaults to false)
15468      */
15469     forceSelection:false,
15470     /**
15471      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15472      * if typeAhead = true (defaults to 250)
15473      */
15474     typeAheadDelay : 250,
15475     /**
15476      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15477      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15478      */
15479     valueNotFoundText : undefined,
15480     /**
15481      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15482      */
15483     blockFocus : false,
15484     
15485     /**
15486      * @cfg {Boolean} disableClear Disable showing of clear button.
15487      */
15488     disableClear : false,
15489     /**
15490      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15491      */
15492     alwaysQuery : false,
15493     
15494     /**
15495      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15496      */
15497     multiple : false,
15498     
15499     /**
15500      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15501      */
15502     invalidClass : "has-warning",
15503     
15504     /**
15505      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15506      */
15507     validClass : "has-success",
15508     
15509     /**
15510      * @cfg {Boolean} specialFilter (true|false) special filter default false
15511      */
15512     specialFilter : false,
15513     
15514     /**
15515      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15516      */
15517     mobileTouchView : true,
15518     
15519     /**
15520      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15521      */
15522     useNativeIOS : false,
15523     
15524     /**
15525      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15526      */
15527     mobile_restrict_height : false,
15528     
15529     ios_options : false,
15530     
15531     //private
15532     addicon : false,
15533     editicon: false,
15534     
15535     page: 0,
15536     hasQuery: false,
15537     append: false,
15538     loadNext: false,
15539     autoFocus : true,
15540     tickable : false,
15541     btnPosition : 'right',
15542     triggerList : true,
15543     showToggleBtn : true,
15544     animate : true,
15545     emptyResultText: 'Empty',
15546     triggerText : 'Select',
15547     emptyTitle : '',
15548     width : false,
15549     
15550     // element that contains real text value.. (when hidden is used..)
15551     
15552     getAutoCreate : function()
15553     {   
15554         var cfg = false;
15555         //render
15556         /*
15557          * Render classic select for iso
15558          */
15559         
15560         if(Roo.isIOS && this.useNativeIOS){
15561             cfg = this.getAutoCreateNativeIOS();
15562             return cfg;
15563         }
15564         
15565         /*
15566          * Touch Devices
15567          */
15568         
15569         if(Roo.isTouch && this.mobileTouchView){
15570             cfg = this.getAutoCreateTouchView();
15571             return cfg;;
15572         }
15573         
15574         /*
15575          *  Normal ComboBox
15576          */
15577         if(!this.tickable){
15578             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15579             return cfg;
15580         }
15581         
15582         /*
15583          *  ComboBox with tickable selections
15584          */
15585              
15586         var align = this.labelAlign || this.parentLabelAlign();
15587         
15588         cfg = {
15589             cls : 'form-group roo-combobox-tickable' //input-group
15590         };
15591         
15592         var btn_text_select = '';
15593         var btn_text_done = '';
15594         var btn_text_cancel = '';
15595         
15596         if (this.btn_text_show) {
15597             btn_text_select = 'Select';
15598             btn_text_done = 'Done';
15599             btn_text_cancel = 'Cancel'; 
15600         }
15601         
15602         var buttons = {
15603             tag : 'div',
15604             cls : 'tickable-buttons',
15605             cn : [
15606                 {
15607                     tag : 'button',
15608                     type : 'button',
15609                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15610                     //html : this.triggerText
15611                     html: btn_text_select
15612                 },
15613                 {
15614                     tag : 'button',
15615                     type : 'button',
15616                     name : 'ok',
15617                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15618                     //html : 'Done'
15619                     html: btn_text_done
15620                 },
15621                 {
15622                     tag : 'button',
15623                     type : 'button',
15624                     name : 'cancel',
15625                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15626                     //html : 'Cancel'
15627                     html: btn_text_cancel
15628                 }
15629             ]
15630         };
15631         
15632         if(this.editable){
15633             buttons.cn.unshift({
15634                 tag: 'input',
15635                 cls: 'roo-select2-search-field-input'
15636             });
15637         }
15638         
15639         var _this = this;
15640         
15641         Roo.each(buttons.cn, function(c){
15642             if (_this.size) {
15643                 c.cls += ' btn-' + _this.size;
15644             }
15645
15646             if (_this.disabled) {
15647                 c.disabled = true;
15648             }
15649         });
15650         
15651         var box = {
15652             tag: 'div',
15653             style : 'display: contents',
15654             cn: [
15655                 {
15656                     tag: 'input',
15657                     type : 'hidden',
15658                     cls: 'form-hidden-field'
15659                 },
15660                 {
15661                     tag: 'ul',
15662                     cls: 'roo-select2-choices',
15663                     cn:[
15664                         {
15665                             tag: 'li',
15666                             cls: 'roo-select2-search-field',
15667                             cn: [
15668                                 buttons
15669                             ]
15670                         }
15671                     ]
15672                 }
15673             ]
15674         };
15675         
15676         var combobox = {
15677             cls: 'roo-select2-container input-group roo-select2-container-multi',
15678             cn: [
15679                 
15680                 box
15681 //                {
15682 //                    tag: 'ul',
15683 //                    cls: 'typeahead typeahead-long dropdown-menu',
15684 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15685 //                }
15686             ]
15687         };
15688         
15689         if(this.hasFeedback && !this.allowBlank){
15690             
15691             var feedback = {
15692                 tag: 'span',
15693                 cls: 'glyphicon form-control-feedback'
15694             };
15695
15696             combobox.cn.push(feedback);
15697         }
15698         
15699         
15700         
15701         var indicator = {
15702             tag : 'i',
15703             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15704             tooltip : 'This field is required'
15705         };
15706         if (Roo.bootstrap.version == 4) {
15707             indicator = {
15708                 tag : 'i',
15709                 style : 'display:none'
15710             };
15711         }
15712         if (align ==='left' && this.fieldLabel.length) {
15713             
15714             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15715             
15716             cfg.cn = [
15717                 indicator,
15718                 {
15719                     tag: 'label',
15720                     'for' :  id,
15721                     cls : 'control-label col-form-label',
15722                     html : this.fieldLabel
15723
15724                 },
15725                 {
15726                     cls : "", 
15727                     cn: [
15728                         combobox
15729                     ]
15730                 }
15731
15732             ];
15733             
15734             var labelCfg = cfg.cn[1];
15735             var contentCfg = cfg.cn[2];
15736             
15737
15738             if(this.indicatorpos == 'right'){
15739                 
15740                 cfg.cn = [
15741                     {
15742                         tag: 'label',
15743                         'for' :  id,
15744                         cls : 'control-label col-form-label',
15745                         cn : [
15746                             {
15747                                 tag : 'span',
15748                                 html : this.fieldLabel
15749                             },
15750                             indicator
15751                         ]
15752                     },
15753                     {
15754                         cls : "",
15755                         cn: [
15756                             combobox
15757                         ]
15758                     }
15759
15760                 ];
15761                 
15762                 
15763                 
15764                 labelCfg = cfg.cn[0];
15765                 contentCfg = cfg.cn[1];
15766             
15767             }
15768             
15769             if(this.labelWidth > 12){
15770                 labelCfg.style = "width: " + this.labelWidth + 'px';
15771             }
15772             if(this.width * 1 > 0){
15773                 contentCfg.style = "width: " + this.width + 'px';
15774             }
15775             if(this.labelWidth < 13 && this.labelmd == 0){
15776                 this.labelmd = this.labelWidth;
15777             }
15778             
15779             if(this.labellg > 0){
15780                 labelCfg.cls += ' col-lg-' + this.labellg;
15781                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15782             }
15783             
15784             if(this.labelmd > 0){
15785                 labelCfg.cls += ' col-md-' + this.labelmd;
15786                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15787             }
15788             
15789             if(this.labelsm > 0){
15790                 labelCfg.cls += ' col-sm-' + this.labelsm;
15791                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15792             }
15793             
15794             if(this.labelxs > 0){
15795                 labelCfg.cls += ' col-xs-' + this.labelxs;
15796                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15797             }
15798                 
15799                 
15800         } else if ( this.fieldLabel.length) {
15801 //                Roo.log(" label");
15802                  cfg.cn = [
15803                    indicator,
15804                     {
15805                         tag: 'label',
15806                         //cls : 'input-group-addon',
15807                         html : this.fieldLabel
15808                     },
15809                     combobox
15810                 ];
15811                 
15812                 if(this.indicatorpos == 'right'){
15813                     cfg.cn = [
15814                         {
15815                             tag: 'label',
15816                             //cls : 'input-group-addon',
15817                             html : this.fieldLabel
15818                         },
15819                         indicator,
15820                         combobox
15821                     ];
15822                     
15823                 }
15824
15825         } else {
15826             
15827 //                Roo.log(" no label && no align");
15828                 cfg = combobox
15829                      
15830                 
15831         }
15832          
15833         var settings=this;
15834         ['xs','sm','md','lg'].map(function(size){
15835             if (settings[size]) {
15836                 cfg.cls += ' col-' + size + '-' + settings[size];
15837             }
15838         });
15839         
15840         return cfg;
15841         
15842     },
15843     
15844     _initEventsCalled : false,
15845     
15846     // private
15847     initEvents: function()
15848     {   
15849         if (this._initEventsCalled) { // as we call render... prevent looping...
15850             return;
15851         }
15852         this._initEventsCalled = true;
15853         
15854         if (!this.store) {
15855             throw "can not find store for combo";
15856         }
15857         
15858         this.indicator = this.indicatorEl();
15859         
15860         this.store = Roo.factory(this.store, Roo.data);
15861         this.store.parent = this;
15862         
15863         // if we are building from html. then this element is so complex, that we can not really
15864         // use the rendered HTML.
15865         // so we have to trash and replace the previous code.
15866         if (Roo.XComponent.build_from_html) {
15867             // remove this element....
15868             var e = this.el.dom, k=0;
15869             while (e ) { e = e.previousSibling;  ++k;}
15870
15871             this.el.remove();
15872             
15873             this.el=false;
15874             this.rendered = false;
15875             
15876             this.render(this.parent().getChildContainer(true), k);
15877         }
15878         
15879         if(Roo.isIOS && this.useNativeIOS){
15880             this.initIOSView();
15881             return;
15882         }
15883         
15884         /*
15885          * Touch Devices
15886          */
15887         
15888         if(Roo.isTouch && this.mobileTouchView){
15889             this.initTouchView();
15890             return;
15891         }
15892         
15893         if(this.tickable){
15894             this.initTickableEvents();
15895             return;
15896         }
15897         
15898         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15899         
15900         if(this.hiddenName){
15901             
15902             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15903             
15904             this.hiddenField.dom.value =
15905                 this.hiddenValue !== undefined ? this.hiddenValue :
15906                 this.value !== undefined ? this.value : '';
15907
15908             // prevent input submission
15909             this.el.dom.removeAttribute('name');
15910             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15911              
15912              
15913         }
15914         //if(Roo.isGecko){
15915         //    this.el.dom.setAttribute('autocomplete', 'off');
15916         //}
15917         
15918         var cls = 'x-combo-list';
15919         
15920         //this.list = new Roo.Layer({
15921         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15922         //});
15923         
15924         var _this = this;
15925         
15926         (function(){
15927             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15928             _this.list.setWidth(lw);
15929         }).defer(100);
15930         
15931         this.list.on('mouseover', this.onViewOver, this);
15932         this.list.on('mousemove', this.onViewMove, this);
15933         this.list.on('scroll', this.onViewScroll, this);
15934         
15935         /*
15936         this.list.swallowEvent('mousewheel');
15937         this.assetHeight = 0;
15938
15939         if(this.title){
15940             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15941             this.assetHeight += this.header.getHeight();
15942         }
15943
15944         this.innerList = this.list.createChild({cls:cls+'-inner'});
15945         this.innerList.on('mouseover', this.onViewOver, this);
15946         this.innerList.on('mousemove', this.onViewMove, this);
15947         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15948         
15949         if(this.allowBlank && !this.pageSize && !this.disableClear){
15950             this.footer = this.list.createChild({cls:cls+'-ft'});
15951             this.pageTb = new Roo.Toolbar(this.footer);
15952            
15953         }
15954         if(this.pageSize){
15955             this.footer = this.list.createChild({cls:cls+'-ft'});
15956             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15957                     {pageSize: this.pageSize});
15958             
15959         }
15960         
15961         if (this.pageTb && this.allowBlank && !this.disableClear) {
15962             var _this = this;
15963             this.pageTb.add(new Roo.Toolbar.Fill(), {
15964                 cls: 'x-btn-icon x-btn-clear',
15965                 text: '&#160;',
15966                 handler: function()
15967                 {
15968                     _this.collapse();
15969                     _this.clearValue();
15970                     _this.onSelect(false, -1);
15971                 }
15972             });
15973         }
15974         if (this.footer) {
15975             this.assetHeight += this.footer.getHeight();
15976         }
15977         */
15978             
15979         if(!this.tpl){
15980             this.tpl = Roo.bootstrap.version == 4 ?
15981                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15982                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15983         }
15984
15985         this.view = new Roo.View(this.list, this.tpl, {
15986             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15987         });
15988         //this.view.wrapEl.setDisplayed(false);
15989         this.view.on('click', this.onViewClick, this);
15990         
15991         
15992         this.store.on('beforeload', this.onBeforeLoad, this);
15993         this.store.on('load', this.onLoad, this);
15994         this.store.on('loadexception', this.onLoadException, this);
15995         /*
15996         if(this.resizable){
15997             this.resizer = new Roo.Resizable(this.list,  {
15998                pinned:true, handles:'se'
15999             });
16000             this.resizer.on('resize', function(r, w, h){
16001                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16002                 this.listWidth = w;
16003                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16004                 this.restrictHeight();
16005             }, this);
16006             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16007         }
16008         */
16009         if(!this.editable){
16010             this.editable = true;
16011             this.setEditable(false);
16012         }
16013         
16014         /*
16015         
16016         if (typeof(this.events.add.listeners) != 'undefined') {
16017             
16018             this.addicon = this.wrap.createChild(
16019                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16020        
16021             this.addicon.on('click', function(e) {
16022                 this.fireEvent('add', this);
16023             }, this);
16024         }
16025         if (typeof(this.events.edit.listeners) != 'undefined') {
16026             
16027             this.editicon = this.wrap.createChild(
16028                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16029             if (this.addicon) {
16030                 this.editicon.setStyle('margin-left', '40px');
16031             }
16032             this.editicon.on('click', function(e) {
16033                 
16034                 // we fire even  if inothing is selected..
16035                 this.fireEvent('edit', this, this.lastData );
16036                 
16037             }, this);
16038         }
16039         */
16040         
16041         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16042             "up" : function(e){
16043                 this.inKeyMode = true;
16044                 this.selectPrev();
16045             },
16046
16047             "down" : function(e){
16048                 if(!this.isExpanded()){
16049                     this.onTriggerClick();
16050                 }else{
16051                     this.inKeyMode = true;
16052                     this.selectNext();
16053                 }
16054             },
16055
16056             "enter" : function(e){
16057 //                this.onViewClick();
16058                 //return true;
16059                 this.collapse();
16060                 
16061                 if(this.fireEvent("specialkey", this, e)){
16062                     this.onViewClick(false);
16063                 }
16064                 
16065                 return true;
16066             },
16067
16068             "esc" : function(e){
16069                 this.collapse();
16070             },
16071
16072             "tab" : function(e){
16073                 this.collapse();
16074                 
16075                 if(this.fireEvent("specialkey", this, e)){
16076                     this.onViewClick(false);
16077                 }
16078                 
16079                 return true;
16080             },
16081
16082             scope : this,
16083
16084             doRelay : function(foo, bar, hname){
16085                 if(hname == 'down' || this.scope.isExpanded()){
16086                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16087                 }
16088                 return true;
16089             },
16090
16091             forceKeyDown: true
16092         });
16093         
16094         
16095         this.queryDelay = Math.max(this.queryDelay || 10,
16096                 this.mode == 'local' ? 10 : 250);
16097         
16098         
16099         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16100         
16101         if(this.typeAhead){
16102             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16103         }
16104         if(this.editable !== false){
16105             this.inputEl().on("keyup", this.onKeyUp, this);
16106         }
16107         if(this.forceSelection){
16108             this.inputEl().on('blur', this.doForce, this);
16109         }
16110         
16111         if(this.multiple){
16112             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16113             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16114         }
16115     },
16116     
16117     initTickableEvents: function()
16118     {   
16119         this.createList();
16120         
16121         if(this.hiddenName){
16122             
16123             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16124             
16125             this.hiddenField.dom.value =
16126                 this.hiddenValue !== undefined ? this.hiddenValue :
16127                 this.value !== undefined ? this.value : '';
16128
16129             // prevent input submission
16130             this.el.dom.removeAttribute('name');
16131             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16132              
16133              
16134         }
16135         
16136 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16137         
16138         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16139         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16140         if(this.triggerList){
16141             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16142         }
16143          
16144         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16145         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16146         
16147         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16148         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16149         
16150         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16151         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16152         
16153         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16154         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16155         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16156         
16157         this.okBtn.hide();
16158         this.cancelBtn.hide();
16159         
16160         var _this = this;
16161         
16162         (function(){
16163             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16164             _this.list.setWidth(lw);
16165         }).defer(100);
16166         
16167         this.list.on('mouseover', this.onViewOver, this);
16168         this.list.on('mousemove', this.onViewMove, this);
16169         
16170         this.list.on('scroll', this.onViewScroll, this);
16171         
16172         if(!this.tpl){
16173             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16174                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16175         }
16176
16177         this.view = new Roo.View(this.list, this.tpl, {
16178             singleSelect:true,
16179             tickable:true,
16180             parent:this,
16181             store: this.store,
16182             selectedClass: this.selectedClass
16183         });
16184         
16185         //this.view.wrapEl.setDisplayed(false);
16186         this.view.on('click', this.onViewClick, this);
16187         
16188         
16189         
16190         this.store.on('beforeload', this.onBeforeLoad, this);
16191         this.store.on('load', this.onLoad, this);
16192         this.store.on('loadexception', this.onLoadException, this);
16193         
16194         if(this.editable){
16195             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16196                 "up" : function(e){
16197                     this.inKeyMode = true;
16198                     this.selectPrev();
16199                 },
16200
16201                 "down" : function(e){
16202                     this.inKeyMode = true;
16203                     this.selectNext();
16204                 },
16205
16206                 "enter" : function(e){
16207                     if(this.fireEvent("specialkey", this, e)){
16208                         this.onViewClick(false);
16209                     }
16210                     
16211                     return true;
16212                 },
16213
16214                 "esc" : function(e){
16215                     this.onTickableFooterButtonClick(e, false, false);
16216                 },
16217
16218                 "tab" : function(e){
16219                     this.fireEvent("specialkey", this, e);
16220                     
16221                     this.onTickableFooterButtonClick(e, false, false);
16222                     
16223                     return true;
16224                 },
16225
16226                 scope : this,
16227
16228                 doRelay : function(e, fn, key){
16229                     if(this.scope.isExpanded()){
16230                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16231                     }
16232                     return true;
16233                 },
16234
16235                 forceKeyDown: true
16236             });
16237         }
16238         
16239         this.queryDelay = Math.max(this.queryDelay || 10,
16240                 this.mode == 'local' ? 10 : 250);
16241         
16242         
16243         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16244         
16245         if(this.typeAhead){
16246             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16247         }
16248         
16249         if(this.editable !== false){
16250             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16251         }
16252         
16253         this.indicator = this.indicatorEl();
16254         
16255         if(this.indicator){
16256             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16257             this.indicator.hide();
16258         }
16259         
16260     },
16261
16262     onDestroy : function(){
16263         if(this.view){
16264             this.view.setStore(null);
16265             this.view.el.removeAllListeners();
16266             this.view.el.remove();
16267             this.view.purgeListeners();
16268         }
16269         if(this.list){
16270             this.list.dom.innerHTML  = '';
16271         }
16272         
16273         if(this.store){
16274             this.store.un('beforeload', this.onBeforeLoad, this);
16275             this.store.un('load', this.onLoad, this);
16276             this.store.un('loadexception', this.onLoadException, this);
16277         }
16278         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16279     },
16280
16281     // private
16282     fireKey : function(e){
16283         if(e.isNavKeyPress() && !this.list.isVisible()){
16284             this.fireEvent("specialkey", this, e);
16285         }
16286     },
16287
16288     // private
16289     onResize: function(w, h)
16290     {
16291         
16292         
16293 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16294 //        
16295 //        if(typeof w != 'number'){
16296 //            // we do not handle it!?!?
16297 //            return;
16298 //        }
16299 //        var tw = this.trigger.getWidth();
16300 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16301 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16302 //        var x = w - tw;
16303 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16304 //            
16305 //        //this.trigger.setStyle('left', x+'px');
16306 //        
16307 //        if(this.list && this.listWidth === undefined){
16308 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16309 //            this.list.setWidth(lw);
16310 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16311 //        }
16312         
16313     
16314         
16315     },
16316
16317     /**
16318      * Allow or prevent the user from directly editing the field text.  If false is passed,
16319      * the user will only be able to select from the items defined in the dropdown list.  This method
16320      * is the runtime equivalent of setting the 'editable' config option at config time.
16321      * @param {Boolean} value True to allow the user to directly edit the field text
16322      */
16323     setEditable : function(value){
16324         if(value == this.editable){
16325             return;
16326         }
16327         this.editable = value;
16328         if(!value){
16329             this.inputEl().dom.setAttribute('readOnly', true);
16330             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16331             this.inputEl().addClass('x-combo-noedit');
16332         }else{
16333             this.inputEl().dom.setAttribute('readOnly', false);
16334             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16335             this.inputEl().removeClass('x-combo-noedit');
16336         }
16337     },
16338
16339     // private
16340     
16341     onBeforeLoad : function(combo,opts){
16342         if(!this.hasFocus){
16343             return;
16344         }
16345          if (!opts.add) {
16346             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16347          }
16348         this.restrictHeight();
16349         this.selectedIndex = -1;
16350     },
16351
16352     // private
16353     onLoad : function(){
16354         
16355         this.hasQuery = false;
16356         
16357         if(!this.hasFocus){
16358             return;
16359         }
16360         
16361         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16362             this.loading.hide();
16363         }
16364         
16365         if(this.store.getCount() > 0){
16366             
16367             this.expand();
16368             this.restrictHeight();
16369             if(this.lastQuery == this.allQuery){
16370                 if(this.editable && !this.tickable){
16371                     this.inputEl().dom.select();
16372                 }
16373                 
16374                 if(
16375                     !this.selectByValue(this.value, true) &&
16376                     this.autoFocus && 
16377                     (
16378                         !this.store.lastOptions ||
16379                         typeof(this.store.lastOptions.add) == 'undefined' || 
16380                         this.store.lastOptions.add != true
16381                     )
16382                 ){
16383                     this.select(0, true);
16384                 }
16385             }else{
16386                 if(this.autoFocus){
16387                     this.selectNext();
16388                 }
16389                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16390                     this.taTask.delay(this.typeAheadDelay);
16391                 }
16392             }
16393         }else{
16394             this.onEmptyResults();
16395         }
16396         
16397         //this.el.focus();
16398     },
16399     // private
16400     onLoadException : function()
16401     {
16402         this.hasQuery = false;
16403         
16404         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16405             this.loading.hide();
16406         }
16407         
16408         if(this.tickable && this.editable){
16409             return;
16410         }
16411         
16412         this.collapse();
16413         // only causes errors at present
16414         //Roo.log(this.store.reader.jsonData);
16415         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16416             // fixme
16417             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16418         //}
16419         
16420         
16421     },
16422     // private
16423     onTypeAhead : function(){
16424         if(this.store.getCount() > 0){
16425             var r = this.store.getAt(0);
16426             var newValue = r.data[this.displayField];
16427             var len = newValue.length;
16428             var selStart = this.getRawValue().length;
16429             
16430             if(selStart != len){
16431                 this.setRawValue(newValue);
16432                 this.selectText(selStart, newValue.length);
16433             }
16434         }
16435     },
16436
16437     // private
16438     onSelect : function(record, index){
16439         
16440         if(this.fireEvent('beforeselect', this, record, index) !== false){
16441         
16442             this.setFromData(index > -1 ? record.data : false);
16443             
16444             this.collapse();
16445             this.fireEvent('select', this, record, index);
16446         }
16447     },
16448
16449     /**
16450      * Returns the currently selected field value or empty string if no value is set.
16451      * @return {String} value The selected value
16452      */
16453     getValue : function()
16454     {
16455         if(Roo.isIOS && this.useNativeIOS){
16456             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16457         }
16458         
16459         if(this.multiple){
16460             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16461         }
16462         
16463         if(this.valueField){
16464             return typeof this.value != 'undefined' ? this.value : '';
16465         }else{
16466             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16467         }
16468     },
16469     
16470     getRawValue : function()
16471     {
16472         if(Roo.isIOS && this.useNativeIOS){
16473             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16474         }
16475         
16476         var v = this.inputEl().getValue();
16477         
16478         return v;
16479     },
16480
16481     /**
16482      * Clears any text/value currently set in the field
16483      */
16484     clearValue : function(){
16485         
16486         if(this.hiddenField){
16487             this.hiddenField.dom.value = '';
16488         }
16489         this.value = '';
16490         this.setRawValue('');
16491         this.lastSelectionText = '';
16492         this.lastData = false;
16493         
16494         var close = this.closeTriggerEl();
16495         
16496         if(close){
16497             close.hide();
16498         }
16499         
16500         this.validate();
16501         
16502     },
16503
16504     /**
16505      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16506      * will be displayed in the field.  If the value does not match the data value of an existing item,
16507      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16508      * Otherwise the field will be blank (although the value will still be set).
16509      * @param {String} value The value to match
16510      */
16511     setValue : function(v)
16512     {
16513         if(Roo.isIOS && this.useNativeIOS){
16514             this.setIOSValue(v);
16515             return;
16516         }
16517         
16518         if(this.multiple){
16519             this.syncValue();
16520             return;
16521         }
16522         
16523         var text = v;
16524         if(this.valueField){
16525             var r = this.findRecord(this.valueField, v);
16526             if(r){
16527                 text = r.data[this.displayField];
16528             }else if(this.valueNotFoundText !== undefined){
16529                 text = this.valueNotFoundText;
16530             }
16531         }
16532         this.lastSelectionText = text;
16533         if(this.hiddenField){
16534             this.hiddenField.dom.value = v;
16535         }
16536         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16537         this.value = v;
16538         
16539         var close = this.closeTriggerEl();
16540         
16541         if(close){
16542             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16543         }
16544         
16545         this.validate();
16546     },
16547     /**
16548      * @property {Object} the last set data for the element
16549      */
16550     
16551     lastData : false,
16552     /**
16553      * Sets the value of the field based on a object which is related to the record format for the store.
16554      * @param {Object} value the value to set as. or false on reset?
16555      */
16556     setFromData : function(o){
16557         
16558         if(this.multiple){
16559             this.addItem(o);
16560             return;
16561         }
16562             
16563         var dv = ''; // display value
16564         var vv = ''; // value value..
16565         this.lastData = o;
16566         if (this.displayField) {
16567             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16568         } else {
16569             // this is an error condition!!!
16570             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16571         }
16572         
16573         if(this.valueField){
16574             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16575         }
16576         
16577         var close = this.closeTriggerEl();
16578         
16579         if(close){
16580             if(dv.length || vv * 1 > 0){
16581                 close.show() ;
16582                 this.blockFocus=true;
16583             } else {
16584                 close.hide();
16585             }             
16586         }
16587         
16588         if(this.hiddenField){
16589             this.hiddenField.dom.value = vv;
16590             
16591             this.lastSelectionText = dv;
16592             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16593             this.value = vv;
16594             return;
16595         }
16596         // no hidden field.. - we store the value in 'value', but still display
16597         // display field!!!!
16598         this.lastSelectionText = dv;
16599         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16600         this.value = vv;
16601         
16602         
16603         
16604     },
16605     // private
16606     reset : function(){
16607         // overridden so that last data is reset..
16608         
16609         if(this.multiple){
16610             this.clearItem();
16611             return;
16612         }
16613         
16614         this.setValue(this.originalValue);
16615         //this.clearInvalid();
16616         this.lastData = false;
16617         if (this.view) {
16618             this.view.clearSelections();
16619         }
16620         
16621         this.validate();
16622     },
16623     // private
16624     findRecord : function(prop, value){
16625         var record;
16626         if(this.store.getCount() > 0){
16627             this.store.each(function(r){
16628                 if(r.data[prop] == value){
16629                     record = r;
16630                     return false;
16631                 }
16632                 return true;
16633             });
16634         }
16635         return record;
16636     },
16637     
16638     getName: function()
16639     {
16640         // returns hidden if it's set..
16641         if (!this.rendered) {return ''};
16642         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16643         
16644     },
16645     // private
16646     onViewMove : function(e, t){
16647         this.inKeyMode = false;
16648     },
16649
16650     // private
16651     onViewOver : function(e, t){
16652         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16653             return;
16654         }
16655         var item = this.view.findItemFromChild(t);
16656         
16657         if(item){
16658             var index = this.view.indexOf(item);
16659             this.select(index, false);
16660         }
16661     },
16662
16663     // private
16664     onViewClick : function(view, doFocus, el, e)
16665     {
16666         var index = this.view.getSelectedIndexes()[0];
16667         
16668         var r = this.store.getAt(index);
16669         
16670         if(this.tickable){
16671             
16672             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16673                 return;
16674             }
16675             
16676             var rm = false;
16677             var _this = this;
16678             
16679             Roo.each(this.tickItems, function(v,k){
16680                 
16681                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16682                     Roo.log(v);
16683                     _this.tickItems.splice(k, 1);
16684                     
16685                     if(typeof(e) == 'undefined' && view == false){
16686                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16687                     }
16688                     
16689                     rm = true;
16690                     return;
16691                 }
16692             });
16693             
16694             if(rm){
16695                 return;
16696             }
16697             
16698             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16699                 this.tickItems.push(r.data);
16700             }
16701             
16702             if(typeof(e) == 'undefined' && view == false){
16703                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16704             }
16705                     
16706             return;
16707         }
16708         
16709         if(r){
16710             this.onSelect(r, index);
16711         }
16712         if(doFocus !== false && !this.blockFocus){
16713             this.inputEl().focus();
16714         }
16715     },
16716
16717     // private
16718     restrictHeight : function(){
16719         //this.innerList.dom.style.height = '';
16720         //var inner = this.innerList.dom;
16721         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16722         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16723         //this.list.beginUpdate();
16724         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16725         this.list.alignTo(this.inputEl(), this.listAlign);
16726         this.list.alignTo(this.inputEl(), this.listAlign);
16727         //this.list.endUpdate();
16728     },
16729
16730     // private
16731     onEmptyResults : function(){
16732         
16733         if(this.tickable && this.editable){
16734             this.hasFocus = false;
16735             this.restrictHeight();
16736             return;
16737         }
16738         
16739         this.collapse();
16740     },
16741
16742     /**
16743      * Returns true if the dropdown list is expanded, else false.
16744      */
16745     isExpanded : function(){
16746         return this.list.isVisible();
16747     },
16748
16749     /**
16750      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16751      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16752      * @param {String} value The data value of the item to select
16753      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16754      * selected item if it is not currently in view (defaults to true)
16755      * @return {Boolean} True if the value matched an item in the list, else false
16756      */
16757     selectByValue : function(v, scrollIntoView){
16758         if(v !== undefined && v !== null){
16759             var r = this.findRecord(this.valueField || this.displayField, v);
16760             if(r){
16761                 this.select(this.store.indexOf(r), scrollIntoView);
16762                 return true;
16763             }
16764         }
16765         return false;
16766     },
16767
16768     /**
16769      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16770      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16771      * @param {Number} index The zero-based index of the list item to select
16772      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16773      * selected item if it is not currently in view (defaults to true)
16774      */
16775     select : function(index, scrollIntoView){
16776         this.selectedIndex = index;
16777         this.view.select(index);
16778         if(scrollIntoView !== false){
16779             var el = this.view.getNode(index);
16780             /*
16781              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16782              */
16783             if(el){
16784                 this.list.scrollChildIntoView(el, false);
16785             }
16786         }
16787     },
16788
16789     // private
16790     selectNext : function(){
16791         var ct = this.store.getCount();
16792         if(ct > 0){
16793             if(this.selectedIndex == -1){
16794                 this.select(0);
16795             }else if(this.selectedIndex < ct-1){
16796                 this.select(this.selectedIndex+1);
16797             }
16798         }
16799     },
16800
16801     // private
16802     selectPrev : function(){
16803         var ct = this.store.getCount();
16804         if(ct > 0){
16805             if(this.selectedIndex == -1){
16806                 this.select(0);
16807             }else if(this.selectedIndex != 0){
16808                 this.select(this.selectedIndex-1);
16809             }
16810         }
16811     },
16812
16813     // private
16814     onKeyUp : function(e){
16815         if(this.editable !== false && !e.isSpecialKey()){
16816             this.lastKey = e.getKey();
16817             this.dqTask.delay(this.queryDelay);
16818         }
16819     },
16820
16821     // private
16822     validateBlur : function(){
16823         return !this.list || !this.list.isVisible();   
16824     },
16825
16826     // private
16827     initQuery : function(){
16828         
16829         var v = this.getRawValue();
16830         
16831         if(this.tickable && this.editable){
16832             v = this.tickableInputEl().getValue();
16833         }
16834         
16835         this.doQuery(v);
16836     },
16837
16838     // private
16839     doForce : function(){
16840         if(this.inputEl().dom.value.length > 0){
16841             this.inputEl().dom.value =
16842                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16843              
16844         }
16845     },
16846
16847     /**
16848      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16849      * query allowing the query action to be canceled if needed.
16850      * @param {String} query The SQL query to execute
16851      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16852      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16853      * saved in the current store (defaults to false)
16854      */
16855     doQuery : function(q, forceAll){
16856         
16857         if(q === undefined || q === null){
16858             q = '';
16859         }
16860         var qe = {
16861             query: q,
16862             forceAll: forceAll,
16863             combo: this,
16864             cancel:false
16865         };
16866         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16867             return false;
16868         }
16869         q = qe.query;
16870         
16871         forceAll = qe.forceAll;
16872         if(forceAll === true || (q.length >= this.minChars)){
16873             
16874             this.hasQuery = true;
16875             
16876             if(this.lastQuery != q || this.alwaysQuery){
16877                 this.lastQuery = q;
16878                 if(this.mode == 'local'){
16879                     this.selectedIndex = -1;
16880                     if(forceAll){
16881                         this.store.clearFilter();
16882                     }else{
16883                         
16884                         if(this.specialFilter){
16885                             this.fireEvent('specialfilter', this);
16886                             this.onLoad();
16887                             return;
16888                         }
16889                         
16890                         this.store.filter(this.displayField, q);
16891                     }
16892                     
16893                     this.store.fireEvent("datachanged", this.store);
16894                     
16895                     this.onLoad();
16896                     
16897                     
16898                 }else{
16899                     
16900                     this.store.baseParams[this.queryParam] = q;
16901                     
16902                     var options = {params : this.getParams(q)};
16903                     
16904                     if(this.loadNext){
16905                         options.add = true;
16906                         options.params.start = this.page * this.pageSize;
16907                     }
16908                     
16909                     this.store.load(options);
16910                     
16911                     /*
16912                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16913                      *  we should expand the list on onLoad
16914                      *  so command out it
16915                      */
16916 //                    this.expand();
16917                 }
16918             }else{
16919                 this.selectedIndex = -1;
16920                 this.onLoad();   
16921             }
16922         }
16923         
16924         this.loadNext = false;
16925     },
16926     
16927     // private
16928     getParams : function(q){
16929         var p = {};
16930         //p[this.queryParam] = q;
16931         
16932         if(this.pageSize){
16933             p.start = 0;
16934             p.limit = this.pageSize;
16935         }
16936         return p;
16937     },
16938
16939     /**
16940      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16941      */
16942     collapse : function(){
16943         if(!this.isExpanded()){
16944             return;
16945         }
16946         
16947         this.list.hide();
16948         
16949         this.hasFocus = false;
16950         
16951         if(this.tickable){
16952             this.okBtn.hide();
16953             this.cancelBtn.hide();
16954             this.trigger.show();
16955             
16956             if(this.editable){
16957                 this.tickableInputEl().dom.value = '';
16958                 this.tickableInputEl().blur();
16959             }
16960             
16961         }
16962         
16963         Roo.get(document).un('mousedown', this.collapseIf, this);
16964         Roo.get(document).un('mousewheel', this.collapseIf, this);
16965         if (!this.editable) {
16966             Roo.get(document).un('keydown', this.listKeyPress, this);
16967         }
16968         this.fireEvent('collapse', this);
16969         
16970         this.validate();
16971     },
16972
16973     // private
16974     collapseIf : function(e){
16975         var in_combo  = e.within(this.el);
16976         var in_list =  e.within(this.list);
16977         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16978         
16979         if (in_combo || in_list || is_list) {
16980             //e.stopPropagation();
16981             return;
16982         }
16983         
16984         if(this.tickable){
16985             this.onTickableFooterButtonClick(e, false, false);
16986         }
16987
16988         this.collapse();
16989         
16990     },
16991
16992     /**
16993      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16994      */
16995     expand : function(){
16996        
16997         if(this.isExpanded() || !this.hasFocus){
16998             return;
16999         }
17000         
17001         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17002         this.list.setWidth(lw);
17003         
17004         Roo.log('expand');
17005         
17006         this.list.show();
17007         
17008         this.restrictHeight();
17009         
17010         if(this.tickable){
17011             
17012             this.tickItems = Roo.apply([], this.item);
17013             
17014             this.okBtn.show();
17015             this.cancelBtn.show();
17016             this.trigger.hide();
17017             
17018             if(this.editable){
17019                 this.tickableInputEl().focus();
17020             }
17021             
17022         }
17023         
17024         Roo.get(document).on('mousedown', this.collapseIf, this);
17025         Roo.get(document).on('mousewheel', this.collapseIf, this);
17026         if (!this.editable) {
17027             Roo.get(document).on('keydown', this.listKeyPress, this);
17028         }
17029         
17030         this.fireEvent('expand', this);
17031     },
17032
17033     // private
17034     // Implements the default empty TriggerField.onTriggerClick function
17035     onTriggerClick : function(e)
17036     {
17037         Roo.log('trigger click');
17038         
17039         if(this.disabled || !this.triggerList){
17040             return;
17041         }
17042         
17043         this.page = 0;
17044         this.loadNext = false;
17045         
17046         if(this.isExpanded()){
17047             this.collapse();
17048             if (!this.blockFocus) {
17049                 this.inputEl().focus();
17050             }
17051             
17052         }else {
17053             this.hasFocus = true;
17054             if(this.triggerAction == 'all') {
17055                 this.doQuery(this.allQuery, true);
17056             } else {
17057                 this.doQuery(this.getRawValue());
17058             }
17059             if (!this.blockFocus) {
17060                 this.inputEl().focus();
17061             }
17062         }
17063     },
17064     
17065     onTickableTriggerClick : function(e)
17066     {
17067         if(this.disabled){
17068             return;
17069         }
17070         
17071         this.page = 0;
17072         this.loadNext = false;
17073         this.hasFocus = true;
17074         
17075         if(this.triggerAction == 'all') {
17076             this.doQuery(this.allQuery, true);
17077         } else {
17078             this.doQuery(this.getRawValue());
17079         }
17080     },
17081     
17082     onSearchFieldClick : function(e)
17083     {
17084         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17085             this.onTickableFooterButtonClick(e, false, false);
17086             return;
17087         }
17088         
17089         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17090             return;
17091         }
17092         
17093         this.page = 0;
17094         this.loadNext = false;
17095         this.hasFocus = true;
17096         
17097         if(this.triggerAction == 'all') {
17098             this.doQuery(this.allQuery, true);
17099         } else {
17100             this.doQuery(this.getRawValue());
17101         }
17102     },
17103     
17104     listKeyPress : function(e)
17105     {
17106         //Roo.log('listkeypress');
17107         // scroll to first matching element based on key pres..
17108         if (e.isSpecialKey()) {
17109             return false;
17110         }
17111         var k = String.fromCharCode(e.getKey()).toUpperCase();
17112         //Roo.log(k);
17113         var match  = false;
17114         var csel = this.view.getSelectedNodes();
17115         var cselitem = false;
17116         if (csel.length) {
17117             var ix = this.view.indexOf(csel[0]);
17118             cselitem  = this.store.getAt(ix);
17119             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17120                 cselitem = false;
17121             }
17122             
17123         }
17124         
17125         this.store.each(function(v) { 
17126             if (cselitem) {
17127                 // start at existing selection.
17128                 if (cselitem.id == v.id) {
17129                     cselitem = false;
17130                 }
17131                 return true;
17132             }
17133                 
17134             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17135                 match = this.store.indexOf(v);
17136                 return false;
17137             }
17138             return true;
17139         }, this);
17140         
17141         if (match === false) {
17142             return true; // no more action?
17143         }
17144         // scroll to?
17145         this.view.select(match);
17146         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17147         sn.scrollIntoView(sn.dom.parentNode, false);
17148     },
17149     
17150     onViewScroll : function(e, t){
17151         
17152         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){
17153             return;
17154         }
17155         
17156         this.hasQuery = true;
17157         
17158         this.loading = this.list.select('.loading', true).first();
17159         
17160         if(this.loading === null){
17161             this.list.createChild({
17162                 tag: 'div',
17163                 cls: 'loading roo-select2-more-results roo-select2-active',
17164                 html: 'Loading more results...'
17165             });
17166             
17167             this.loading = this.list.select('.loading', true).first();
17168             
17169             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17170             
17171             this.loading.hide();
17172         }
17173         
17174         this.loading.show();
17175         
17176         var _combo = this;
17177         
17178         this.page++;
17179         this.loadNext = true;
17180         
17181         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17182         
17183         return;
17184     },
17185     
17186     addItem : function(o)
17187     {   
17188         var dv = ''; // display value
17189         
17190         if (this.displayField) {
17191             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17192         } else {
17193             // this is an error condition!!!
17194             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17195         }
17196         
17197         if(!dv.length){
17198             return;
17199         }
17200         
17201         var choice = this.choices.createChild({
17202             tag: 'li',
17203             cls: 'roo-select2-search-choice',
17204             cn: [
17205                 {
17206                     tag: 'div',
17207                     html: dv
17208                 },
17209                 {
17210                     tag: 'a',
17211                     href: '#',
17212                     cls: 'roo-select2-search-choice-close fa fa-times',
17213                     tabindex: '-1'
17214                 }
17215             ]
17216             
17217         }, this.searchField);
17218         
17219         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17220         
17221         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17222         
17223         this.item.push(o);
17224         
17225         this.lastData = o;
17226         
17227         this.syncValue();
17228         
17229         this.inputEl().dom.value = '';
17230         
17231         this.validate();
17232     },
17233     
17234     onRemoveItem : function(e, _self, o)
17235     {
17236         e.preventDefault();
17237         
17238         this.lastItem = Roo.apply([], this.item);
17239         
17240         var index = this.item.indexOf(o.data) * 1;
17241         
17242         if( index < 0){
17243             Roo.log('not this item?!');
17244             return;
17245         }
17246         
17247         this.item.splice(index, 1);
17248         o.item.remove();
17249         
17250         this.syncValue();
17251         
17252         this.fireEvent('remove', this, e);
17253         
17254         this.validate();
17255         
17256     },
17257     
17258     syncValue : function()
17259     {
17260         if(!this.item.length){
17261             this.clearValue();
17262             return;
17263         }
17264             
17265         var value = [];
17266         var _this = this;
17267         Roo.each(this.item, function(i){
17268             if(_this.valueField){
17269                 value.push(i[_this.valueField]);
17270                 return;
17271             }
17272
17273             value.push(i);
17274         });
17275
17276         this.value = value.join(',');
17277
17278         if(this.hiddenField){
17279             this.hiddenField.dom.value = this.value;
17280         }
17281         
17282         this.store.fireEvent("datachanged", this.store);
17283         
17284         this.validate();
17285     },
17286     
17287     clearItem : function()
17288     {
17289         if(!this.multiple){
17290             return;
17291         }
17292         
17293         this.item = [];
17294         
17295         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17296            c.remove();
17297         });
17298         
17299         this.syncValue();
17300         
17301         this.validate();
17302         
17303         if(this.tickable && !Roo.isTouch){
17304             this.view.refresh();
17305         }
17306     },
17307     
17308     inputEl: function ()
17309     {
17310         if(Roo.isIOS && this.useNativeIOS){
17311             return this.el.select('select.roo-ios-select', true).first();
17312         }
17313         
17314         if(Roo.isTouch && this.mobileTouchView){
17315             return this.el.select('input.form-control',true).first();
17316         }
17317         
17318         if(this.tickable){
17319             return this.searchField;
17320         }
17321         
17322         return this.el.select('input.form-control',true).first();
17323     },
17324     
17325     onTickableFooterButtonClick : function(e, btn, el)
17326     {
17327         e.preventDefault();
17328         
17329         this.lastItem = Roo.apply([], this.item);
17330         
17331         if(btn && btn.name == 'cancel'){
17332             this.tickItems = Roo.apply([], this.item);
17333             this.collapse();
17334             return;
17335         }
17336         
17337         this.clearItem();
17338         
17339         var _this = this;
17340         
17341         Roo.each(this.tickItems, function(o){
17342             _this.addItem(o);
17343         });
17344         
17345         this.collapse();
17346         
17347     },
17348     
17349     validate : function()
17350     {
17351         if(this.getVisibilityEl().hasClass('hidden')){
17352             return true;
17353         }
17354         
17355         var v = this.getRawValue();
17356         
17357         if(this.multiple){
17358             v = this.getValue();
17359         }
17360         
17361         if(this.disabled || this.allowBlank || v.length){
17362             this.markValid();
17363             return true;
17364         }
17365         
17366         this.markInvalid();
17367         return false;
17368     },
17369     
17370     tickableInputEl : function()
17371     {
17372         if(!this.tickable || !this.editable){
17373             return this.inputEl();
17374         }
17375         
17376         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17377     },
17378     
17379     
17380     getAutoCreateTouchView : function()
17381     {
17382         var id = Roo.id();
17383         
17384         var cfg = {
17385             cls: 'form-group' //input-group
17386         };
17387         
17388         var input =  {
17389             tag: 'input',
17390             id : id,
17391             type : this.inputType,
17392             cls : 'form-control x-combo-noedit',
17393             autocomplete: 'new-password',
17394             placeholder : this.placeholder || '',
17395             readonly : true
17396         };
17397         
17398         if (this.name) {
17399             input.name = this.name;
17400         }
17401         
17402         if (this.size) {
17403             input.cls += ' input-' + this.size;
17404         }
17405         
17406         if (this.disabled) {
17407             input.disabled = true;
17408         }
17409         
17410         var inputblock = {
17411             cls : 'roo-combobox-wrap',
17412             cn : [
17413                 input
17414             ]
17415         };
17416         
17417         if(this.before){
17418             inputblock.cls += ' input-group';
17419             
17420             inputblock.cn.unshift({
17421                 tag :'span',
17422                 cls : 'input-group-addon input-group-prepend input-group-text',
17423                 html : this.before
17424             });
17425         }
17426         
17427         if(this.removable && !this.multiple){
17428             inputblock.cls += ' roo-removable';
17429             
17430             inputblock.cn.push({
17431                 tag: 'button',
17432                 html : 'x',
17433                 cls : 'roo-combo-removable-btn close'
17434             });
17435         }
17436
17437         if(this.hasFeedback && !this.allowBlank){
17438             
17439             inputblock.cls += ' has-feedback';
17440             
17441             inputblock.cn.push({
17442                 tag: 'span',
17443                 cls: 'glyphicon form-control-feedback'
17444             });
17445             
17446         }
17447         
17448         if (this.after) {
17449             
17450             inputblock.cls += (this.before) ? '' : ' input-group';
17451             
17452             inputblock.cn.push({
17453                 tag :'span',
17454                 cls : 'input-group-addon input-group-append input-group-text',
17455                 html : this.after
17456             });
17457         }
17458
17459         
17460         var ibwrap = inputblock;
17461         
17462         if(this.multiple){
17463             ibwrap = {
17464                 tag: 'ul',
17465                 cls: 'roo-select2-choices',
17466                 cn:[
17467                     {
17468                         tag: 'li',
17469                         cls: 'roo-select2-search-field',
17470                         cn: [
17471
17472                             inputblock
17473                         ]
17474                     }
17475                 ]
17476             };
17477         
17478             
17479         }
17480         
17481         var combobox = {
17482             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17483             cn: [
17484                 {
17485                     tag: 'input',
17486                     type : 'hidden',
17487                     cls: 'form-hidden-field'
17488                 },
17489                 ibwrap
17490             ]
17491         };
17492         
17493         if(!this.multiple && this.showToggleBtn){
17494             
17495             var caret = {
17496                 cls: 'caret'
17497             };
17498             
17499             if (this.caret != false) {
17500                 caret = {
17501                      tag: 'i',
17502                      cls: 'fa fa-' + this.caret
17503                 };
17504                 
17505             }
17506             
17507             combobox.cn.push({
17508                 tag :'span',
17509                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17510                 cn : [
17511                     Roo.bootstrap.version == 3 ? caret : '',
17512                     {
17513                         tag: 'span',
17514                         cls: 'combobox-clear',
17515                         cn  : [
17516                             {
17517                                 tag : 'i',
17518                                 cls: 'icon-remove'
17519                             }
17520                         ]
17521                     }
17522                 ]
17523
17524             })
17525         }
17526         
17527         if(this.multiple){
17528             combobox.cls += ' roo-select2-container-multi';
17529         }
17530         
17531         var align = this.labelAlign || this.parentLabelAlign();
17532         
17533         if (align ==='left' && this.fieldLabel.length) {
17534
17535             cfg.cn = [
17536                 {
17537                    tag : 'i',
17538                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17539                    tooltip : 'This field is required'
17540                 },
17541                 {
17542                     tag: 'label',
17543                     cls : 'control-label col-form-label',
17544                     html : this.fieldLabel
17545
17546                 },
17547                 {
17548                     cls : 'roo-combobox-wrap ', 
17549                     cn: [
17550                         combobox
17551                     ]
17552                 }
17553             ];
17554             
17555             var labelCfg = cfg.cn[1];
17556             var contentCfg = cfg.cn[2];
17557             
17558
17559             if(this.indicatorpos == 'right'){
17560                 cfg.cn = [
17561                     {
17562                         tag: 'label',
17563                         'for' :  id,
17564                         cls : 'control-label col-form-label',
17565                         cn : [
17566                             {
17567                                 tag : 'span',
17568                                 html : this.fieldLabel
17569                             },
17570                             {
17571                                 tag : 'i',
17572                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17573                                 tooltip : 'This field is required'
17574                             }
17575                         ]
17576                     },
17577                     {
17578                         cls : "roo-combobox-wrap ",
17579                         cn: [
17580                             combobox
17581                         ]
17582                     }
17583
17584                 ];
17585                 
17586                 labelCfg = cfg.cn[0];
17587                 contentCfg = cfg.cn[1];
17588             }
17589             
17590            
17591             
17592             if(this.labelWidth > 12){
17593                 labelCfg.style = "width: " + this.labelWidth + 'px';
17594             }
17595            
17596             if(this.labelWidth < 13 && this.labelmd == 0){
17597                 this.labelmd = this.labelWidth;
17598             }
17599             
17600             if(this.labellg > 0){
17601                 labelCfg.cls += ' col-lg-' + this.labellg;
17602                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17603             }
17604             
17605             if(this.labelmd > 0){
17606                 labelCfg.cls += ' col-md-' + this.labelmd;
17607                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17608             }
17609             
17610             if(this.labelsm > 0){
17611                 labelCfg.cls += ' col-sm-' + this.labelsm;
17612                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17613             }
17614             
17615             if(this.labelxs > 0){
17616                 labelCfg.cls += ' col-xs-' + this.labelxs;
17617                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17618             }
17619                 
17620                 
17621         } else if ( this.fieldLabel.length) {
17622             cfg.cn = [
17623                 {
17624                    tag : 'i',
17625                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17626                    tooltip : 'This field is required'
17627                 },
17628                 {
17629                     tag: 'label',
17630                     cls : 'control-label',
17631                     html : this.fieldLabel
17632
17633                 },
17634                 {
17635                     cls : '', 
17636                     cn: [
17637                         combobox
17638                     ]
17639                 }
17640             ];
17641             
17642             if(this.indicatorpos == 'right'){
17643                 cfg.cn = [
17644                     {
17645                         tag: 'label',
17646                         cls : 'control-label',
17647                         html : this.fieldLabel,
17648                         cn : [
17649                             {
17650                                tag : 'i',
17651                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17652                                tooltip : 'This field is required'
17653                             }
17654                         ]
17655                     },
17656                     {
17657                         cls : '', 
17658                         cn: [
17659                             combobox
17660                         ]
17661                     }
17662                 ];
17663             }
17664         } else {
17665             cfg.cn = combobox;    
17666         }
17667         
17668         
17669         var settings = this;
17670         
17671         ['xs','sm','md','lg'].map(function(size){
17672             if (settings[size]) {
17673                 cfg.cls += ' col-' + size + '-' + settings[size];
17674             }
17675         });
17676         
17677         return cfg;
17678     },
17679     
17680     initTouchView : function()
17681     {
17682         this.renderTouchView();
17683         
17684         this.touchViewEl.on('scroll', function(){
17685             this.el.dom.scrollTop = 0;
17686         }, this);
17687         
17688         this.originalValue = this.getValue();
17689         
17690         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17691         
17692         this.inputEl().on("click", this.showTouchView, this);
17693         if (this.triggerEl) {
17694             this.triggerEl.on("click", this.showTouchView, this);
17695         }
17696         
17697         
17698         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17699         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17700         
17701         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17702         
17703         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17704         this.store.on('load', this.onTouchViewLoad, this);
17705         this.store.on('loadexception', this.onTouchViewLoadException, this);
17706         
17707         if(this.hiddenName){
17708             
17709             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17710             
17711             this.hiddenField.dom.value =
17712                 this.hiddenValue !== undefined ? this.hiddenValue :
17713                 this.value !== undefined ? this.value : '';
17714         
17715             this.el.dom.removeAttribute('name');
17716             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17717         }
17718         
17719         if(this.multiple){
17720             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17721             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17722         }
17723         
17724         if(this.removable && !this.multiple){
17725             var close = this.closeTriggerEl();
17726             if(close){
17727                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17728                 close.on('click', this.removeBtnClick, this, close);
17729             }
17730         }
17731         /*
17732          * fix the bug in Safari iOS8
17733          */
17734         this.inputEl().on("focus", function(e){
17735             document.activeElement.blur();
17736         }, this);
17737         
17738         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17739         
17740         return;
17741         
17742         
17743     },
17744     
17745     renderTouchView : function()
17746     {
17747         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17748         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17749         
17750         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17751         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17752         
17753         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17754         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17755         this.touchViewBodyEl.setStyle('overflow', 'auto');
17756         
17757         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17758         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17759         
17760         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17761         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17762         
17763     },
17764     
17765     showTouchView : function()
17766     {
17767         if(this.disabled){
17768             return;
17769         }
17770         
17771         this.touchViewHeaderEl.hide();
17772
17773         if(this.modalTitle.length){
17774             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17775             this.touchViewHeaderEl.show();
17776         }
17777
17778         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17779         this.touchViewEl.show();
17780
17781         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17782         
17783         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17784         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17785
17786         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17787
17788         if(this.modalTitle.length){
17789             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17790         }
17791         
17792         this.touchViewBodyEl.setHeight(bodyHeight);
17793
17794         if(this.animate){
17795             var _this = this;
17796             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17797         }else{
17798             this.touchViewEl.addClass(['in','show']);
17799         }
17800         
17801         if(this._touchViewMask){
17802             Roo.get(document.body).addClass("x-body-masked");
17803             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17804             this._touchViewMask.setStyle('z-index', 10000);
17805             this._touchViewMask.addClass('show');
17806         }
17807         
17808         this.doTouchViewQuery();
17809         
17810     },
17811     
17812     hideTouchView : function()
17813     {
17814         this.touchViewEl.removeClass(['in','show']);
17815
17816         if(this.animate){
17817             var _this = this;
17818             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17819         }else{
17820             this.touchViewEl.setStyle('display', 'none');
17821         }
17822         
17823         if(this._touchViewMask){
17824             this._touchViewMask.removeClass('show');
17825             Roo.get(document.body).removeClass("x-body-masked");
17826         }
17827     },
17828     
17829     setTouchViewValue : function()
17830     {
17831         if(this.multiple){
17832             this.clearItem();
17833         
17834             var _this = this;
17835
17836             Roo.each(this.tickItems, function(o){
17837                 this.addItem(o);
17838             }, this);
17839         }
17840         
17841         this.hideTouchView();
17842     },
17843     
17844     doTouchViewQuery : function()
17845     {
17846         var qe = {
17847             query: '',
17848             forceAll: true,
17849             combo: this,
17850             cancel:false
17851         };
17852         
17853         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17854             return false;
17855         }
17856         
17857         if(!this.alwaysQuery || this.mode == 'local'){
17858             this.onTouchViewLoad();
17859             return;
17860         }
17861         
17862         this.store.load();
17863     },
17864     
17865     onTouchViewBeforeLoad : function(combo,opts)
17866     {
17867         return;
17868     },
17869
17870     // private
17871     onTouchViewLoad : function()
17872     {
17873         if(this.store.getCount() < 1){
17874             this.onTouchViewEmptyResults();
17875             return;
17876         }
17877         
17878         this.clearTouchView();
17879         
17880         var rawValue = this.getRawValue();
17881         
17882         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17883         
17884         this.tickItems = [];
17885         
17886         this.store.data.each(function(d, rowIndex){
17887             var row = this.touchViewListGroup.createChild(template);
17888             
17889             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17890                 row.addClass(d.data.cls);
17891             }
17892             
17893             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17894                 var cfg = {
17895                     data : d.data,
17896                     html : d.data[this.displayField]
17897                 };
17898                 
17899                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17900                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17901                 }
17902             }
17903             row.removeClass('selected');
17904             if(!this.multiple && this.valueField &&
17905                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17906             {
17907                 // radio buttons..
17908                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17909                 row.addClass('selected');
17910             }
17911             
17912             if(this.multiple && this.valueField &&
17913                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17914             {
17915                 
17916                 // checkboxes...
17917                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17918                 this.tickItems.push(d.data);
17919             }
17920             
17921             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17922             
17923         }, this);
17924         
17925         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17926         
17927         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17928
17929         if(this.modalTitle.length){
17930             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17931         }
17932
17933         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17934         
17935         if(this.mobile_restrict_height && listHeight < bodyHeight){
17936             this.touchViewBodyEl.setHeight(listHeight);
17937         }
17938         
17939         var _this = this;
17940         
17941         if(firstChecked && listHeight > bodyHeight){
17942             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17943         }
17944         
17945     },
17946     
17947     onTouchViewLoadException : function()
17948     {
17949         this.hideTouchView();
17950     },
17951     
17952     onTouchViewEmptyResults : function()
17953     {
17954         this.clearTouchView();
17955         
17956         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17957         
17958         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17959         
17960     },
17961     
17962     clearTouchView : function()
17963     {
17964         this.touchViewListGroup.dom.innerHTML = '';
17965     },
17966     
17967     onTouchViewClick : function(e, el, o)
17968     {
17969         e.preventDefault();
17970         
17971         var row = o.row;
17972         var rowIndex = o.rowIndex;
17973         
17974         var r = this.store.getAt(rowIndex);
17975         
17976         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17977             
17978             if(!this.multiple){
17979                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17980                     c.dom.removeAttribute('checked');
17981                 }, this);
17982
17983                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17984
17985                 this.setFromData(r.data);
17986
17987                 var close = this.closeTriggerEl();
17988
17989                 if(close){
17990                     close.show();
17991                 }
17992
17993                 this.hideTouchView();
17994
17995                 this.fireEvent('select', this, r, rowIndex);
17996
17997                 return;
17998             }
17999
18000             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18001                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18002                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18003                 return;
18004             }
18005
18006             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18007             this.addItem(r.data);
18008             this.tickItems.push(r.data);
18009         }
18010     },
18011     
18012     getAutoCreateNativeIOS : function()
18013     {
18014         var cfg = {
18015             cls: 'form-group' //input-group,
18016         };
18017         
18018         var combobox =  {
18019             tag: 'select',
18020             cls : 'roo-ios-select'
18021         };
18022         
18023         if (this.name) {
18024             combobox.name = this.name;
18025         }
18026         
18027         if (this.disabled) {
18028             combobox.disabled = true;
18029         }
18030         
18031         var settings = this;
18032         
18033         ['xs','sm','md','lg'].map(function(size){
18034             if (settings[size]) {
18035                 cfg.cls += ' col-' + size + '-' + settings[size];
18036             }
18037         });
18038         
18039         cfg.cn = combobox;
18040         
18041         return cfg;
18042         
18043     },
18044     
18045     initIOSView : function()
18046     {
18047         this.store.on('load', this.onIOSViewLoad, this);
18048         
18049         return;
18050     },
18051     
18052     onIOSViewLoad : function()
18053     {
18054         if(this.store.getCount() < 1){
18055             return;
18056         }
18057         
18058         this.clearIOSView();
18059         
18060         if(this.allowBlank) {
18061             
18062             var default_text = '-- SELECT --';
18063             
18064             if(this.placeholder.length){
18065                 default_text = this.placeholder;
18066             }
18067             
18068             if(this.emptyTitle.length){
18069                 default_text += ' - ' + this.emptyTitle + ' -';
18070             }
18071             
18072             var opt = this.inputEl().createChild({
18073                 tag: 'option',
18074                 value : 0,
18075                 html : default_text
18076             });
18077             
18078             var o = {};
18079             o[this.valueField] = 0;
18080             o[this.displayField] = default_text;
18081             
18082             this.ios_options.push({
18083                 data : o,
18084                 el : opt
18085             });
18086             
18087         }
18088         
18089         this.store.data.each(function(d, rowIndex){
18090             
18091             var html = '';
18092             
18093             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18094                 html = d.data[this.displayField];
18095             }
18096             
18097             var value = '';
18098             
18099             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18100                 value = d.data[this.valueField];
18101             }
18102             
18103             var option = {
18104                 tag: 'option',
18105                 value : value,
18106                 html : html
18107             };
18108             
18109             if(this.value == d.data[this.valueField]){
18110                 option['selected'] = true;
18111             }
18112             
18113             var opt = this.inputEl().createChild(option);
18114             
18115             this.ios_options.push({
18116                 data : d.data,
18117                 el : opt
18118             });
18119             
18120         }, this);
18121         
18122         this.inputEl().on('change', function(){
18123            this.fireEvent('select', this);
18124         }, this);
18125         
18126     },
18127     
18128     clearIOSView: function()
18129     {
18130         this.inputEl().dom.innerHTML = '';
18131         
18132         this.ios_options = [];
18133     },
18134     
18135     setIOSValue: function(v)
18136     {
18137         this.value = v;
18138         
18139         if(!this.ios_options){
18140             return;
18141         }
18142         
18143         Roo.each(this.ios_options, function(opts){
18144            
18145            opts.el.dom.removeAttribute('selected');
18146            
18147            if(opts.data[this.valueField] != v){
18148                return;
18149            }
18150            
18151            opts.el.dom.setAttribute('selected', true);
18152            
18153         }, this);
18154     }
18155
18156     /** 
18157     * @cfg {Boolean} grow 
18158     * @hide 
18159     */
18160     /** 
18161     * @cfg {Number} growMin 
18162     * @hide 
18163     */
18164     /** 
18165     * @cfg {Number} growMax 
18166     * @hide 
18167     */
18168     /**
18169      * @hide
18170      * @method autoSize
18171      */
18172 });
18173
18174 Roo.apply(Roo.bootstrap.ComboBox,  {
18175     
18176     header : {
18177         tag: 'div',
18178         cls: 'modal-header',
18179         cn: [
18180             {
18181                 tag: 'h4',
18182                 cls: 'modal-title'
18183             }
18184         ]
18185     },
18186     
18187     body : {
18188         tag: 'div',
18189         cls: 'modal-body',
18190         cn: [
18191             {
18192                 tag: 'ul',
18193                 cls: 'list-group'
18194             }
18195         ]
18196     },
18197     
18198     listItemRadio : {
18199         tag: 'li',
18200         cls: 'list-group-item',
18201         cn: [
18202             {
18203                 tag: 'span',
18204                 cls: 'roo-combobox-list-group-item-value'
18205             },
18206             {
18207                 tag: 'div',
18208                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18209                 cn: [
18210                     {
18211                         tag: 'input',
18212                         type: 'radio'
18213                     },
18214                     {
18215                         tag: 'label'
18216                     }
18217                 ]
18218             }
18219         ]
18220     },
18221     
18222     listItemCheckbox : {
18223         tag: 'li',
18224         cls: 'list-group-item',
18225         cn: [
18226             {
18227                 tag: 'span',
18228                 cls: 'roo-combobox-list-group-item-value'
18229             },
18230             {
18231                 tag: 'div',
18232                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18233                 cn: [
18234                     {
18235                         tag: 'input',
18236                         type: 'checkbox'
18237                     },
18238                     {
18239                         tag: 'label'
18240                     }
18241                 ]
18242             }
18243         ]
18244     },
18245     
18246     emptyResult : {
18247         tag: 'div',
18248         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18249     },
18250     
18251     footer : {
18252         tag: 'div',
18253         cls: 'modal-footer',
18254         cn: [
18255             {
18256                 tag: 'div',
18257                 cls: 'row',
18258                 cn: [
18259                     {
18260                         tag: 'div',
18261                         cls: 'col-xs-6 text-left',
18262                         cn: {
18263                             tag: 'button',
18264                             cls: 'btn btn-danger roo-touch-view-cancel',
18265                             html: 'Cancel'
18266                         }
18267                     },
18268                     {
18269                         tag: 'div',
18270                         cls: 'col-xs-6 text-right',
18271                         cn: {
18272                             tag: 'button',
18273                             cls: 'btn btn-success roo-touch-view-ok',
18274                             html: 'OK'
18275                         }
18276                     }
18277                 ]
18278             }
18279         ]
18280         
18281     }
18282 });
18283
18284 Roo.apply(Roo.bootstrap.ComboBox,  {
18285     
18286     touchViewTemplate : {
18287         tag: 'div',
18288         cls: 'modal fade roo-combobox-touch-view',
18289         cn: [
18290             {
18291                 tag: 'div',
18292                 cls: 'modal-dialog',
18293                 style : 'position:fixed', // we have to fix position....
18294                 cn: [
18295                     {
18296                         tag: 'div',
18297                         cls: 'modal-content',
18298                         cn: [
18299                             Roo.bootstrap.ComboBox.header,
18300                             Roo.bootstrap.ComboBox.body,
18301                             Roo.bootstrap.ComboBox.footer
18302                         ]
18303                     }
18304                 ]
18305             }
18306         ]
18307     }
18308 });/*
18309  * Based on:
18310  * Ext JS Library 1.1.1
18311  * Copyright(c) 2006-2007, Ext JS, LLC.
18312  *
18313  * Originally Released Under LGPL - original licence link has changed is not relivant.
18314  *
18315  * Fork - LGPL
18316  * <script type="text/javascript">
18317  */
18318
18319 /**
18320  * @class Roo.View
18321  * @extends Roo.util.Observable
18322  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18323  * This class also supports single and multi selection modes. <br>
18324  * Create a data model bound view:
18325  <pre><code>
18326  var store = new Roo.data.Store(...);
18327
18328  var view = new Roo.View({
18329     el : "my-element",
18330     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18331  
18332     singleSelect: true,
18333     selectedClass: "ydataview-selected",
18334     store: store
18335  });
18336
18337  // listen for node click?
18338  view.on("click", function(vw, index, node, e){
18339  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18340  });
18341
18342  // load XML data
18343  dataModel.load("foobar.xml");
18344  </code></pre>
18345  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18346  * <br><br>
18347  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18348  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18349  * 
18350  * Note: old style constructor is still suported (container, template, config)
18351  * 
18352  * @constructor
18353  * Create a new View
18354  * @param {Object} config The config object
18355  * 
18356  */
18357 Roo.View = function(config, depreciated_tpl, depreciated_config){
18358     
18359     this.parent = false;
18360     
18361     if (typeof(depreciated_tpl) == 'undefined') {
18362         // new way.. - universal constructor.
18363         Roo.apply(this, config);
18364         this.el  = Roo.get(this.el);
18365     } else {
18366         // old format..
18367         this.el  = Roo.get(config);
18368         this.tpl = depreciated_tpl;
18369         Roo.apply(this, depreciated_config);
18370     }
18371     this.wrapEl  = this.el.wrap().wrap();
18372     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18373     
18374     
18375     if(typeof(this.tpl) == "string"){
18376         this.tpl = new Roo.Template(this.tpl);
18377     } else {
18378         // support xtype ctors..
18379         this.tpl = new Roo.factory(this.tpl, Roo);
18380     }
18381     
18382     
18383     this.tpl.compile();
18384     
18385     /** @private */
18386     this.addEvents({
18387         /**
18388          * @event beforeclick
18389          * Fires before a click is processed. Returns false to cancel the default action.
18390          * @param {Roo.View} this
18391          * @param {Number} index The index of the target node
18392          * @param {HTMLElement} node The target node
18393          * @param {Roo.EventObject} e The raw event object
18394          */
18395             "beforeclick" : true,
18396         /**
18397          * @event click
18398          * Fires when a template node is clicked.
18399          * @param {Roo.View} this
18400          * @param {Number} index The index of the target node
18401          * @param {HTMLElement} node The target node
18402          * @param {Roo.EventObject} e The raw event object
18403          */
18404             "click" : true,
18405         /**
18406          * @event dblclick
18407          * Fires when a template node is double clicked.
18408          * @param {Roo.View} this
18409          * @param {Number} index The index of the target node
18410          * @param {HTMLElement} node The target node
18411          * @param {Roo.EventObject} e The raw event object
18412          */
18413             "dblclick" : true,
18414         /**
18415          * @event contextmenu
18416          * Fires when a template node is right clicked.
18417          * @param {Roo.View} this
18418          * @param {Number} index The index of the target node
18419          * @param {HTMLElement} node The target node
18420          * @param {Roo.EventObject} e The raw event object
18421          */
18422             "contextmenu" : true,
18423         /**
18424          * @event selectionchange
18425          * Fires when the selected nodes change.
18426          * @param {Roo.View} this
18427          * @param {Array} selections Array of the selected nodes
18428          */
18429             "selectionchange" : true,
18430     
18431         /**
18432          * @event beforeselect
18433          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18434          * @param {Roo.View} this
18435          * @param {HTMLElement} node The node to be selected
18436          * @param {Array} selections Array of currently selected nodes
18437          */
18438             "beforeselect" : true,
18439         /**
18440          * @event preparedata
18441          * Fires on every row to render, to allow you to change the data.
18442          * @param {Roo.View} this
18443          * @param {Object} data to be rendered (change this)
18444          */
18445           "preparedata" : true
18446           
18447           
18448         });
18449
18450
18451
18452     this.el.on({
18453         "click": this.onClick,
18454         "dblclick": this.onDblClick,
18455         "contextmenu": this.onContextMenu,
18456         scope:this
18457     });
18458
18459     this.selections = [];
18460     this.nodes = [];
18461     this.cmp = new Roo.CompositeElementLite([]);
18462     if(this.store){
18463         this.store = Roo.factory(this.store, Roo.data);
18464         this.setStore(this.store, true);
18465     }
18466     
18467     if ( this.footer && this.footer.xtype) {
18468            
18469          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18470         
18471         this.footer.dataSource = this.store;
18472         this.footer.container = fctr;
18473         this.footer = Roo.factory(this.footer, Roo);
18474         fctr.insertFirst(this.el);
18475         
18476         // this is a bit insane - as the paging toolbar seems to detach the el..
18477 //        dom.parentNode.parentNode.parentNode
18478          // they get detached?
18479     }
18480     
18481     
18482     Roo.View.superclass.constructor.call(this);
18483     
18484     
18485 };
18486
18487 Roo.extend(Roo.View, Roo.util.Observable, {
18488     
18489      /**
18490      * @cfg {Roo.data.Store} store Data store to load data from.
18491      */
18492     store : false,
18493     
18494     /**
18495      * @cfg {String|Roo.Element} el The container element.
18496      */
18497     el : '',
18498     
18499     /**
18500      * @cfg {String|Roo.Template} tpl The template used by this View 
18501      */
18502     tpl : false,
18503     /**
18504      * @cfg {String} dataName the named area of the template to use as the data area
18505      *                          Works with domtemplates roo-name="name"
18506      */
18507     dataName: false,
18508     /**
18509      * @cfg {String} selectedClass The css class to add to selected nodes
18510      */
18511     selectedClass : "x-view-selected",
18512      /**
18513      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18514      */
18515     emptyText : "",
18516     
18517     /**
18518      * @cfg {String} text to display on mask (default Loading)
18519      */
18520     mask : false,
18521     /**
18522      * @cfg {Boolean} multiSelect Allow multiple selection
18523      */
18524     multiSelect : false,
18525     /**
18526      * @cfg {Boolean} singleSelect Allow single selection
18527      */
18528     singleSelect:  false,
18529     
18530     /**
18531      * @cfg {Boolean} toggleSelect - selecting 
18532      */
18533     toggleSelect : false,
18534     
18535     /**
18536      * @cfg {Boolean} tickable - selecting 
18537      */
18538     tickable : false,
18539     
18540     /**
18541      * Returns the element this view is bound to.
18542      * @return {Roo.Element}
18543      */
18544     getEl : function(){
18545         return this.wrapEl;
18546     },
18547     
18548     
18549
18550     /**
18551      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18552      */
18553     refresh : function(){
18554         //Roo.log('refresh');
18555         var t = this.tpl;
18556         
18557         // if we are using something like 'domtemplate', then
18558         // the what gets used is:
18559         // t.applySubtemplate(NAME, data, wrapping data..)
18560         // the outer template then get' applied with
18561         //     the store 'extra data'
18562         // and the body get's added to the
18563         //      roo-name="data" node?
18564         //      <span class='roo-tpl-{name}'></span> ?????
18565         
18566         
18567         
18568         this.clearSelections();
18569         this.el.update("");
18570         var html = [];
18571         var records = this.store.getRange();
18572         if(records.length < 1) {
18573             
18574             // is this valid??  = should it render a template??
18575             
18576             this.el.update(this.emptyText);
18577             return;
18578         }
18579         var el = this.el;
18580         if (this.dataName) {
18581             this.el.update(t.apply(this.store.meta)); //????
18582             el = this.el.child('.roo-tpl-' + this.dataName);
18583         }
18584         
18585         for(var i = 0, len = records.length; i < len; i++){
18586             var data = this.prepareData(records[i].data, i, records[i]);
18587             this.fireEvent("preparedata", this, data, i, records[i]);
18588             
18589             var d = Roo.apply({}, data);
18590             
18591             if(this.tickable){
18592                 Roo.apply(d, {'roo-id' : Roo.id()});
18593                 
18594                 var _this = this;
18595             
18596                 Roo.each(this.parent.item, function(item){
18597                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18598                         return;
18599                     }
18600                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18601                 });
18602             }
18603             
18604             html[html.length] = Roo.util.Format.trim(
18605                 this.dataName ?
18606                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18607                     t.apply(d)
18608             );
18609         }
18610         
18611         
18612         
18613         el.update(html.join(""));
18614         this.nodes = el.dom.childNodes;
18615         this.updateIndexes(0);
18616     },
18617     
18618
18619     /**
18620      * Function to override to reformat the data that is sent to
18621      * the template for each node.
18622      * DEPRICATED - use the preparedata event handler.
18623      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18624      * a JSON object for an UpdateManager bound view).
18625      */
18626     prepareData : function(data, index, record)
18627     {
18628         this.fireEvent("preparedata", this, data, index, record);
18629         return data;
18630     },
18631
18632     onUpdate : function(ds, record){
18633         // Roo.log('on update');   
18634         this.clearSelections();
18635         var index = this.store.indexOf(record);
18636         var n = this.nodes[index];
18637         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18638         n.parentNode.removeChild(n);
18639         this.updateIndexes(index, index);
18640     },
18641
18642     
18643     
18644 // --------- FIXME     
18645     onAdd : function(ds, records, index)
18646     {
18647         //Roo.log(['on Add', ds, records, index] );        
18648         this.clearSelections();
18649         if(this.nodes.length == 0){
18650             this.refresh();
18651             return;
18652         }
18653         var n = this.nodes[index];
18654         for(var i = 0, len = records.length; i < len; i++){
18655             var d = this.prepareData(records[i].data, i, records[i]);
18656             if(n){
18657                 this.tpl.insertBefore(n, d);
18658             }else{
18659                 
18660                 this.tpl.append(this.el, d);
18661             }
18662         }
18663         this.updateIndexes(index);
18664     },
18665
18666     onRemove : function(ds, record, index){
18667        // Roo.log('onRemove');
18668         this.clearSelections();
18669         var el = this.dataName  ?
18670             this.el.child('.roo-tpl-' + this.dataName) :
18671             this.el; 
18672         
18673         el.dom.removeChild(this.nodes[index]);
18674         this.updateIndexes(index);
18675     },
18676
18677     /**
18678      * Refresh an individual node.
18679      * @param {Number} index
18680      */
18681     refreshNode : function(index){
18682         this.onUpdate(this.store, this.store.getAt(index));
18683     },
18684
18685     updateIndexes : function(startIndex, endIndex){
18686         var ns = this.nodes;
18687         startIndex = startIndex || 0;
18688         endIndex = endIndex || ns.length - 1;
18689         for(var i = startIndex; i <= endIndex; i++){
18690             ns[i].nodeIndex = i;
18691         }
18692     },
18693
18694     /**
18695      * Changes the data store this view uses and refresh the view.
18696      * @param {Store} store
18697      */
18698     setStore : function(store, initial){
18699         if(!initial && this.store){
18700             this.store.un("datachanged", this.refresh);
18701             this.store.un("add", this.onAdd);
18702             this.store.un("remove", this.onRemove);
18703             this.store.un("update", this.onUpdate);
18704             this.store.un("clear", this.refresh);
18705             this.store.un("beforeload", this.onBeforeLoad);
18706             this.store.un("load", this.onLoad);
18707             this.store.un("loadexception", this.onLoad);
18708         }
18709         if(store){
18710           
18711             store.on("datachanged", this.refresh, this);
18712             store.on("add", this.onAdd, this);
18713             store.on("remove", this.onRemove, this);
18714             store.on("update", this.onUpdate, this);
18715             store.on("clear", this.refresh, this);
18716             store.on("beforeload", this.onBeforeLoad, this);
18717             store.on("load", this.onLoad, this);
18718             store.on("loadexception", this.onLoad, this);
18719         }
18720         
18721         if(store){
18722             this.refresh();
18723         }
18724     },
18725     /**
18726      * onbeforeLoad - masks the loading area.
18727      *
18728      */
18729     onBeforeLoad : function(store,opts)
18730     {
18731          //Roo.log('onBeforeLoad');   
18732         if (!opts.add) {
18733             this.el.update("");
18734         }
18735         this.el.mask(this.mask ? this.mask : "Loading" ); 
18736     },
18737     onLoad : function ()
18738     {
18739         this.el.unmask();
18740     },
18741     
18742
18743     /**
18744      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18745      * @param {HTMLElement} node
18746      * @return {HTMLElement} The template node
18747      */
18748     findItemFromChild : function(node){
18749         var el = this.dataName  ?
18750             this.el.child('.roo-tpl-' + this.dataName,true) :
18751             this.el.dom; 
18752         
18753         if(!node || node.parentNode == el){
18754                     return node;
18755             }
18756             var p = node.parentNode;
18757             while(p && p != el){
18758             if(p.parentNode == el){
18759                 return p;
18760             }
18761             p = p.parentNode;
18762         }
18763             return null;
18764     },
18765
18766     /** @ignore */
18767     onClick : function(e){
18768         var item = this.findItemFromChild(e.getTarget());
18769         if(item){
18770             var index = this.indexOf(item);
18771             if(this.onItemClick(item, index, e) !== false){
18772                 this.fireEvent("click", this, index, item, e);
18773             }
18774         }else{
18775             this.clearSelections();
18776         }
18777     },
18778
18779     /** @ignore */
18780     onContextMenu : function(e){
18781         var item = this.findItemFromChild(e.getTarget());
18782         if(item){
18783             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18784         }
18785     },
18786
18787     /** @ignore */
18788     onDblClick : function(e){
18789         var item = this.findItemFromChild(e.getTarget());
18790         if(item){
18791             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18792         }
18793     },
18794
18795     onItemClick : function(item, index, e)
18796     {
18797         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18798             return false;
18799         }
18800         if (this.toggleSelect) {
18801             var m = this.isSelected(item) ? 'unselect' : 'select';
18802             //Roo.log(m);
18803             var _t = this;
18804             _t[m](item, true, false);
18805             return true;
18806         }
18807         if(this.multiSelect || this.singleSelect){
18808             if(this.multiSelect && e.shiftKey && this.lastSelection){
18809                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18810             }else{
18811                 this.select(item, this.multiSelect && e.ctrlKey);
18812                 this.lastSelection = item;
18813             }
18814             
18815             if(!this.tickable){
18816                 e.preventDefault();
18817             }
18818             
18819         }
18820         return true;
18821     },
18822
18823     /**
18824      * Get the number of selected nodes.
18825      * @return {Number}
18826      */
18827     getSelectionCount : function(){
18828         return this.selections.length;
18829     },
18830
18831     /**
18832      * Get the currently selected nodes.
18833      * @return {Array} An array of HTMLElements
18834      */
18835     getSelectedNodes : function(){
18836         return this.selections;
18837     },
18838
18839     /**
18840      * Get the indexes of the selected nodes.
18841      * @return {Array}
18842      */
18843     getSelectedIndexes : function(){
18844         var indexes = [], s = this.selections;
18845         for(var i = 0, len = s.length; i < len; i++){
18846             indexes.push(s[i].nodeIndex);
18847         }
18848         return indexes;
18849     },
18850
18851     /**
18852      * Clear all selections
18853      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18854      */
18855     clearSelections : function(suppressEvent){
18856         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18857             this.cmp.elements = this.selections;
18858             this.cmp.removeClass(this.selectedClass);
18859             this.selections = [];
18860             if(!suppressEvent){
18861                 this.fireEvent("selectionchange", this, this.selections);
18862             }
18863         }
18864     },
18865
18866     /**
18867      * Returns true if the passed node is selected
18868      * @param {HTMLElement/Number} node The node or node index
18869      * @return {Boolean}
18870      */
18871     isSelected : function(node){
18872         var s = this.selections;
18873         if(s.length < 1){
18874             return false;
18875         }
18876         node = this.getNode(node);
18877         return s.indexOf(node) !== -1;
18878     },
18879
18880     /**
18881      * Selects nodes.
18882      * @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
18883      * @param {Boolean} keepExisting (optional) true to keep existing selections
18884      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18885      */
18886     select : function(nodeInfo, keepExisting, suppressEvent){
18887         if(nodeInfo instanceof Array){
18888             if(!keepExisting){
18889                 this.clearSelections(true);
18890             }
18891             for(var i = 0, len = nodeInfo.length; i < len; i++){
18892                 this.select(nodeInfo[i], true, true);
18893             }
18894             return;
18895         } 
18896         var node = this.getNode(nodeInfo);
18897         if(!node || this.isSelected(node)){
18898             return; // already selected.
18899         }
18900         if(!keepExisting){
18901             this.clearSelections(true);
18902         }
18903         
18904         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18905             Roo.fly(node).addClass(this.selectedClass);
18906             this.selections.push(node);
18907             if(!suppressEvent){
18908                 this.fireEvent("selectionchange", this, this.selections);
18909             }
18910         }
18911         
18912         
18913     },
18914       /**
18915      * Unselects nodes.
18916      * @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
18917      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18918      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18919      */
18920     unselect : function(nodeInfo, keepExisting, suppressEvent)
18921     {
18922         if(nodeInfo instanceof Array){
18923             Roo.each(this.selections, function(s) {
18924                 this.unselect(s, nodeInfo);
18925             }, this);
18926             return;
18927         }
18928         var node = this.getNode(nodeInfo);
18929         if(!node || !this.isSelected(node)){
18930             //Roo.log("not selected");
18931             return; // not selected.
18932         }
18933         // fireevent???
18934         var ns = [];
18935         Roo.each(this.selections, function(s) {
18936             if (s == node ) {
18937                 Roo.fly(node).removeClass(this.selectedClass);
18938
18939                 return;
18940             }
18941             ns.push(s);
18942         },this);
18943         
18944         this.selections= ns;
18945         this.fireEvent("selectionchange", this, this.selections);
18946     },
18947
18948     /**
18949      * Gets a template node.
18950      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18951      * @return {HTMLElement} The node or null if it wasn't found
18952      */
18953     getNode : function(nodeInfo){
18954         if(typeof nodeInfo == "string"){
18955             return document.getElementById(nodeInfo);
18956         }else if(typeof nodeInfo == "number"){
18957             return this.nodes[nodeInfo];
18958         }
18959         return nodeInfo;
18960     },
18961
18962     /**
18963      * Gets a range template nodes.
18964      * @param {Number} startIndex
18965      * @param {Number} endIndex
18966      * @return {Array} An array of nodes
18967      */
18968     getNodes : function(start, end){
18969         var ns = this.nodes;
18970         start = start || 0;
18971         end = typeof end == "undefined" ? ns.length - 1 : end;
18972         var nodes = [];
18973         if(start <= end){
18974             for(var i = start; i <= end; i++){
18975                 nodes.push(ns[i]);
18976             }
18977         } else{
18978             for(var i = start; i >= end; i--){
18979                 nodes.push(ns[i]);
18980             }
18981         }
18982         return nodes;
18983     },
18984
18985     /**
18986      * Finds the index of the passed node
18987      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18988      * @return {Number} The index of the node or -1
18989      */
18990     indexOf : function(node){
18991         node = this.getNode(node);
18992         if(typeof node.nodeIndex == "number"){
18993             return node.nodeIndex;
18994         }
18995         var ns = this.nodes;
18996         for(var i = 0, len = ns.length; i < len; i++){
18997             if(ns[i] == node){
18998                 return i;
18999             }
19000         }
19001         return -1;
19002     }
19003 });
19004 /*
19005  * - LGPL
19006  *
19007  * based on jquery fullcalendar
19008  * 
19009  */
19010
19011 Roo.bootstrap = Roo.bootstrap || {};
19012 /**
19013  * @class Roo.bootstrap.Calendar
19014  * @extends Roo.bootstrap.Component
19015  * Bootstrap Calendar class
19016  * @cfg {Boolean} loadMask (true|false) default false
19017  * @cfg {Object} header generate the user specific header of the calendar, default false
19018
19019  * @constructor
19020  * Create a new Container
19021  * @param {Object} config The config object
19022  */
19023
19024
19025
19026 Roo.bootstrap.Calendar = function(config){
19027     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19028      this.addEvents({
19029         /**
19030              * @event select
19031              * Fires when a date is selected
19032              * @param {DatePicker} this
19033              * @param {Date} date The selected date
19034              */
19035         'select': true,
19036         /**
19037              * @event monthchange
19038              * Fires when the displayed month changes 
19039              * @param {DatePicker} this
19040              * @param {Date} date The selected month
19041              */
19042         'monthchange': true,
19043         /**
19044              * @event evententer
19045              * Fires when mouse over an event
19046              * @param {Calendar} this
19047              * @param {event} Event
19048              */
19049         'evententer': true,
19050         /**
19051              * @event eventleave
19052              * Fires when the mouse leaves an
19053              * @param {Calendar} this
19054              * @param {event}
19055              */
19056         'eventleave': true,
19057         /**
19058              * @event eventclick
19059              * Fires when the mouse click an
19060              * @param {Calendar} this
19061              * @param {event}
19062              */
19063         'eventclick': true
19064         
19065     });
19066
19067 };
19068
19069 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19070     
19071      /**
19072      * @cfg {Number} startDay
19073      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19074      */
19075     startDay : 0,
19076     
19077     loadMask : false,
19078     
19079     header : false,
19080       
19081     getAutoCreate : function(){
19082         
19083         
19084         var fc_button = function(name, corner, style, content ) {
19085             return Roo.apply({},{
19086                 tag : 'span',
19087                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19088                          (corner.length ?
19089                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19090                             ''
19091                         ),
19092                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19093                 unselectable: 'on'
19094             });
19095         };
19096         
19097         var header = {};
19098         
19099         if(!this.header){
19100             header = {
19101                 tag : 'table',
19102                 cls : 'fc-header',
19103                 style : 'width:100%',
19104                 cn : [
19105                     {
19106                         tag: 'tr',
19107                         cn : [
19108                             {
19109                                 tag : 'td',
19110                                 cls : 'fc-header-left',
19111                                 cn : [
19112                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19113                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19114                                     { tag: 'span', cls: 'fc-header-space' },
19115                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19116
19117
19118                                 ]
19119                             },
19120
19121                             {
19122                                 tag : 'td',
19123                                 cls : 'fc-header-center',
19124                                 cn : [
19125                                     {
19126                                         tag: 'span',
19127                                         cls: 'fc-header-title',
19128                                         cn : {
19129                                             tag: 'H2',
19130                                             html : 'month / year'
19131                                         }
19132                                     }
19133
19134                                 ]
19135                             },
19136                             {
19137                                 tag : 'td',
19138                                 cls : 'fc-header-right',
19139                                 cn : [
19140                               /*      fc_button('month', 'left', '', 'month' ),
19141                                     fc_button('week', '', '', 'week' ),
19142                                     fc_button('day', 'right', '', 'day' )
19143                                 */    
19144
19145                                 ]
19146                             }
19147
19148                         ]
19149                     }
19150                 ]
19151             };
19152         }
19153         
19154         header = this.header;
19155         
19156        
19157         var cal_heads = function() {
19158             var ret = [];
19159             // fixme - handle this.
19160             
19161             for (var i =0; i < Date.dayNames.length; i++) {
19162                 var d = Date.dayNames[i];
19163                 ret.push({
19164                     tag: 'th',
19165                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19166                     html : d.substring(0,3)
19167                 });
19168                 
19169             }
19170             ret[0].cls += ' fc-first';
19171             ret[6].cls += ' fc-last';
19172             return ret;
19173         };
19174         var cal_cell = function(n) {
19175             return  {
19176                 tag: 'td',
19177                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19178                 cn : [
19179                     {
19180                         cn : [
19181                             {
19182                                 cls: 'fc-day-number',
19183                                 html: 'D'
19184                             },
19185                             {
19186                                 cls: 'fc-day-content',
19187                              
19188                                 cn : [
19189                                      {
19190                                         style: 'position: relative;' // height: 17px;
19191                                     }
19192                                 ]
19193                             }
19194                             
19195                             
19196                         ]
19197                     }
19198                 ]
19199                 
19200             }
19201         };
19202         var cal_rows = function() {
19203             
19204             var ret = [];
19205             for (var r = 0; r < 6; r++) {
19206                 var row= {
19207                     tag : 'tr',
19208                     cls : 'fc-week',
19209                     cn : []
19210                 };
19211                 
19212                 for (var i =0; i < Date.dayNames.length; i++) {
19213                     var d = Date.dayNames[i];
19214                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19215
19216                 }
19217                 row.cn[0].cls+=' fc-first';
19218                 row.cn[0].cn[0].style = 'min-height:90px';
19219                 row.cn[6].cls+=' fc-last';
19220                 ret.push(row);
19221                 
19222             }
19223             ret[0].cls += ' fc-first';
19224             ret[4].cls += ' fc-prev-last';
19225             ret[5].cls += ' fc-last';
19226             return ret;
19227             
19228         };
19229         
19230         var cal_table = {
19231             tag: 'table',
19232             cls: 'fc-border-separate',
19233             style : 'width:100%',
19234             cellspacing  : 0,
19235             cn : [
19236                 { 
19237                     tag: 'thead',
19238                     cn : [
19239                         { 
19240                             tag: 'tr',
19241                             cls : 'fc-first fc-last',
19242                             cn : cal_heads()
19243                         }
19244                     ]
19245                 },
19246                 { 
19247                     tag: 'tbody',
19248                     cn : cal_rows()
19249                 }
19250                   
19251             ]
19252         };
19253          
19254          var cfg = {
19255             cls : 'fc fc-ltr',
19256             cn : [
19257                 header,
19258                 {
19259                     cls : 'fc-content',
19260                     style : "position: relative;",
19261                     cn : [
19262                         {
19263                             cls : 'fc-view fc-view-month fc-grid',
19264                             style : 'position: relative',
19265                             unselectable : 'on',
19266                             cn : [
19267                                 {
19268                                     cls : 'fc-event-container',
19269                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19270                                 },
19271                                 cal_table
19272                             ]
19273                         }
19274                     ]
19275     
19276                 }
19277            ] 
19278             
19279         };
19280         
19281          
19282         
19283         return cfg;
19284     },
19285     
19286     
19287     initEvents : function()
19288     {
19289         if(!this.store){
19290             throw "can not find store for calendar";
19291         }
19292         
19293         var mark = {
19294             tag: "div",
19295             cls:"x-dlg-mask",
19296             style: "text-align:center",
19297             cn: [
19298                 {
19299                     tag: "div",
19300                     style: "background-color:white;width:50%;margin:250 auto",
19301                     cn: [
19302                         {
19303                             tag: "img",
19304                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19305                         },
19306                         {
19307                             tag: "span",
19308                             html: "Loading"
19309                         }
19310                         
19311                     ]
19312                 }
19313             ]
19314         };
19315         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19316         
19317         var size = this.el.select('.fc-content', true).first().getSize();
19318         this.maskEl.setSize(size.width, size.height);
19319         this.maskEl.enableDisplayMode("block");
19320         if(!this.loadMask){
19321             this.maskEl.hide();
19322         }
19323         
19324         this.store = Roo.factory(this.store, Roo.data);
19325         this.store.on('load', this.onLoad, this);
19326         this.store.on('beforeload', this.onBeforeLoad, this);
19327         
19328         this.resize();
19329         
19330         this.cells = this.el.select('.fc-day',true);
19331         //Roo.log(this.cells);
19332         this.textNodes = this.el.query('.fc-day-number');
19333         this.cells.addClassOnOver('fc-state-hover');
19334         
19335         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19336         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19337         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19338         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19339         
19340         this.on('monthchange', this.onMonthChange, this);
19341         
19342         this.update(new Date().clearTime());
19343     },
19344     
19345     resize : function() {
19346         var sz  = this.el.getSize();
19347         
19348         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19349         this.el.select('.fc-day-content div',true).setHeight(34);
19350     },
19351     
19352     
19353     // private
19354     showPrevMonth : function(e){
19355         this.update(this.activeDate.add("mo", -1));
19356     },
19357     showToday : function(e){
19358         this.update(new Date().clearTime());
19359     },
19360     // private
19361     showNextMonth : function(e){
19362         this.update(this.activeDate.add("mo", 1));
19363     },
19364
19365     // private
19366     showPrevYear : function(){
19367         this.update(this.activeDate.add("y", -1));
19368     },
19369
19370     // private
19371     showNextYear : function(){
19372         this.update(this.activeDate.add("y", 1));
19373     },
19374
19375     
19376    // private
19377     update : function(date)
19378     {
19379         var vd = this.activeDate;
19380         this.activeDate = date;
19381 //        if(vd && this.el){
19382 //            var t = date.getTime();
19383 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19384 //                Roo.log('using add remove');
19385 //                
19386 //                this.fireEvent('monthchange', this, date);
19387 //                
19388 //                this.cells.removeClass("fc-state-highlight");
19389 //                this.cells.each(function(c){
19390 //                   if(c.dateValue == t){
19391 //                       c.addClass("fc-state-highlight");
19392 //                       setTimeout(function(){
19393 //                            try{c.dom.firstChild.focus();}catch(e){}
19394 //                       }, 50);
19395 //                       return false;
19396 //                   }
19397 //                   return true;
19398 //                });
19399 //                return;
19400 //            }
19401 //        }
19402         
19403         var days = date.getDaysInMonth();
19404         
19405         var firstOfMonth = date.getFirstDateOfMonth();
19406         var startingPos = firstOfMonth.getDay()-this.startDay;
19407         
19408         if(startingPos < this.startDay){
19409             startingPos += 7;
19410         }
19411         
19412         var pm = date.add(Date.MONTH, -1);
19413         var prevStart = pm.getDaysInMonth()-startingPos;
19414 //        
19415         this.cells = this.el.select('.fc-day',true);
19416         this.textNodes = this.el.query('.fc-day-number');
19417         this.cells.addClassOnOver('fc-state-hover');
19418         
19419         var cells = this.cells.elements;
19420         var textEls = this.textNodes;
19421         
19422         Roo.each(cells, function(cell){
19423             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19424         });
19425         
19426         days += startingPos;
19427
19428         // convert everything to numbers so it's fast
19429         var day = 86400000;
19430         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19431         //Roo.log(d);
19432         //Roo.log(pm);
19433         //Roo.log(prevStart);
19434         
19435         var today = new Date().clearTime().getTime();
19436         var sel = date.clearTime().getTime();
19437         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19438         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19439         var ddMatch = this.disabledDatesRE;
19440         var ddText = this.disabledDatesText;
19441         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19442         var ddaysText = this.disabledDaysText;
19443         var format = this.format;
19444         
19445         var setCellClass = function(cal, cell){
19446             cell.row = 0;
19447             cell.events = [];
19448             cell.more = [];
19449             //Roo.log('set Cell Class');
19450             cell.title = "";
19451             var t = d.getTime();
19452             
19453             //Roo.log(d);
19454             
19455             cell.dateValue = t;
19456             if(t == today){
19457                 cell.className += " fc-today";
19458                 cell.className += " fc-state-highlight";
19459                 cell.title = cal.todayText;
19460             }
19461             if(t == sel){
19462                 // disable highlight in other month..
19463                 //cell.className += " fc-state-highlight";
19464                 
19465             }
19466             // disabling
19467             if(t < min) {
19468                 cell.className = " fc-state-disabled";
19469                 cell.title = cal.minText;
19470                 return;
19471             }
19472             if(t > max) {
19473                 cell.className = " fc-state-disabled";
19474                 cell.title = cal.maxText;
19475                 return;
19476             }
19477             if(ddays){
19478                 if(ddays.indexOf(d.getDay()) != -1){
19479                     cell.title = ddaysText;
19480                     cell.className = " fc-state-disabled";
19481                 }
19482             }
19483             if(ddMatch && format){
19484                 var fvalue = d.dateFormat(format);
19485                 if(ddMatch.test(fvalue)){
19486                     cell.title = ddText.replace("%0", fvalue);
19487                     cell.className = " fc-state-disabled";
19488                 }
19489             }
19490             
19491             if (!cell.initialClassName) {
19492                 cell.initialClassName = cell.dom.className;
19493             }
19494             
19495             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19496         };
19497
19498         var i = 0;
19499         
19500         for(; i < startingPos; i++) {
19501             textEls[i].innerHTML = (++prevStart);
19502             d.setDate(d.getDate()+1);
19503             
19504             cells[i].className = "fc-past fc-other-month";
19505             setCellClass(this, cells[i]);
19506         }
19507         
19508         var intDay = 0;
19509         
19510         for(; i < days; i++){
19511             intDay = i - startingPos + 1;
19512             textEls[i].innerHTML = (intDay);
19513             d.setDate(d.getDate()+1);
19514             
19515             cells[i].className = ''; // "x-date-active";
19516             setCellClass(this, cells[i]);
19517         }
19518         var extraDays = 0;
19519         
19520         for(; i < 42; i++) {
19521             textEls[i].innerHTML = (++extraDays);
19522             d.setDate(d.getDate()+1);
19523             
19524             cells[i].className = "fc-future fc-other-month";
19525             setCellClass(this, cells[i]);
19526         }
19527         
19528         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19529         
19530         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19531         
19532         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19533         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19534         
19535         if(totalRows != 6){
19536             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19537             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19538         }
19539         
19540         this.fireEvent('monthchange', this, date);
19541         
19542         
19543         /*
19544         if(!this.internalRender){
19545             var main = this.el.dom.firstChild;
19546             var w = main.offsetWidth;
19547             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19548             Roo.fly(main).setWidth(w);
19549             this.internalRender = true;
19550             // opera does not respect the auto grow header center column
19551             // then, after it gets a width opera refuses to recalculate
19552             // without a second pass
19553             if(Roo.isOpera && !this.secondPass){
19554                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19555                 this.secondPass = true;
19556                 this.update.defer(10, this, [date]);
19557             }
19558         }
19559         */
19560         
19561     },
19562     
19563     findCell : function(dt) {
19564         dt = dt.clearTime().getTime();
19565         var ret = false;
19566         this.cells.each(function(c){
19567             //Roo.log("check " +c.dateValue + '?=' + dt);
19568             if(c.dateValue == dt){
19569                 ret = c;
19570                 return false;
19571             }
19572             return true;
19573         });
19574         
19575         return ret;
19576     },
19577     
19578     findCells : function(ev) {
19579         var s = ev.start.clone().clearTime().getTime();
19580        // Roo.log(s);
19581         var e= ev.end.clone().clearTime().getTime();
19582        // Roo.log(e);
19583         var ret = [];
19584         this.cells.each(function(c){
19585              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19586             
19587             if(c.dateValue > e){
19588                 return ;
19589             }
19590             if(c.dateValue < s){
19591                 return ;
19592             }
19593             ret.push(c);
19594         });
19595         
19596         return ret;    
19597     },
19598     
19599 //    findBestRow: function(cells)
19600 //    {
19601 //        var ret = 0;
19602 //        
19603 //        for (var i =0 ; i < cells.length;i++) {
19604 //            ret  = Math.max(cells[i].rows || 0,ret);
19605 //        }
19606 //        return ret;
19607 //        
19608 //    },
19609     
19610     
19611     addItem : function(ev)
19612     {
19613         // look for vertical location slot in
19614         var cells = this.findCells(ev);
19615         
19616 //        ev.row = this.findBestRow(cells);
19617         
19618         // work out the location.
19619         
19620         var crow = false;
19621         var rows = [];
19622         for(var i =0; i < cells.length; i++) {
19623             
19624             cells[i].row = cells[0].row;
19625             
19626             if(i == 0){
19627                 cells[i].row = cells[i].row + 1;
19628             }
19629             
19630             if (!crow) {
19631                 crow = {
19632                     start : cells[i],
19633                     end :  cells[i]
19634                 };
19635                 continue;
19636             }
19637             if (crow.start.getY() == cells[i].getY()) {
19638                 // on same row.
19639                 crow.end = cells[i];
19640                 continue;
19641             }
19642             // different row.
19643             rows.push(crow);
19644             crow = {
19645                 start: cells[i],
19646                 end : cells[i]
19647             };
19648             
19649         }
19650         
19651         rows.push(crow);
19652         ev.els = [];
19653         ev.rows = rows;
19654         ev.cells = cells;
19655         
19656         cells[0].events.push(ev);
19657         
19658         this.calevents.push(ev);
19659     },
19660     
19661     clearEvents: function() {
19662         
19663         if(!this.calevents){
19664             return;
19665         }
19666         
19667         Roo.each(this.cells.elements, function(c){
19668             c.row = 0;
19669             c.events = [];
19670             c.more = [];
19671         });
19672         
19673         Roo.each(this.calevents, function(e) {
19674             Roo.each(e.els, function(el) {
19675                 el.un('mouseenter' ,this.onEventEnter, this);
19676                 el.un('mouseleave' ,this.onEventLeave, this);
19677                 el.remove();
19678             },this);
19679         },this);
19680         
19681         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19682             e.remove();
19683         });
19684         
19685     },
19686     
19687     renderEvents: function()
19688     {   
19689         var _this = this;
19690         
19691         this.cells.each(function(c) {
19692             
19693             if(c.row < 5){
19694                 return;
19695             }
19696             
19697             var ev = c.events;
19698             
19699             var r = 4;
19700             if(c.row != c.events.length){
19701                 r = 4 - (4 - (c.row - c.events.length));
19702             }
19703             
19704             c.events = ev.slice(0, r);
19705             c.more = ev.slice(r);
19706             
19707             if(c.more.length && c.more.length == 1){
19708                 c.events.push(c.more.pop());
19709             }
19710             
19711             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19712             
19713         });
19714             
19715         this.cells.each(function(c) {
19716             
19717             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19718             
19719             
19720             for (var e = 0; e < c.events.length; e++){
19721                 var ev = c.events[e];
19722                 var rows = ev.rows;
19723                 
19724                 for(var i = 0; i < rows.length; i++) {
19725                 
19726                     // how many rows should it span..
19727
19728                     var  cfg = {
19729                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19730                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19731
19732                         unselectable : "on",
19733                         cn : [
19734                             {
19735                                 cls: 'fc-event-inner',
19736                                 cn : [
19737     //                                {
19738     //                                  tag:'span',
19739     //                                  cls: 'fc-event-time',
19740     //                                  html : cells.length > 1 ? '' : ev.time
19741     //                                },
19742                                     {
19743                                       tag:'span',
19744                                       cls: 'fc-event-title',
19745                                       html : String.format('{0}', ev.title)
19746                                     }
19747
19748
19749                                 ]
19750                             },
19751                             {
19752                                 cls: 'ui-resizable-handle ui-resizable-e',
19753                                 html : '&nbsp;&nbsp;&nbsp'
19754                             }
19755
19756                         ]
19757                     };
19758
19759                     if (i == 0) {
19760                         cfg.cls += ' fc-event-start';
19761                     }
19762                     if ((i+1) == rows.length) {
19763                         cfg.cls += ' fc-event-end';
19764                     }
19765
19766                     var ctr = _this.el.select('.fc-event-container',true).first();
19767                     var cg = ctr.createChild(cfg);
19768
19769                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19770                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19771
19772                     var r = (c.more.length) ? 1 : 0;
19773                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19774                     cg.setWidth(ebox.right - sbox.x -2);
19775
19776                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19777                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19778                     cg.on('click', _this.onEventClick, _this, ev);
19779
19780                     ev.els.push(cg);
19781                     
19782                 }
19783                 
19784             }
19785             
19786             
19787             if(c.more.length){
19788                 var  cfg = {
19789                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19790                     style : 'position: absolute',
19791                     unselectable : "on",
19792                     cn : [
19793                         {
19794                             cls: 'fc-event-inner',
19795                             cn : [
19796                                 {
19797                                   tag:'span',
19798                                   cls: 'fc-event-title',
19799                                   html : 'More'
19800                                 }
19801
19802
19803                             ]
19804                         },
19805                         {
19806                             cls: 'ui-resizable-handle ui-resizable-e',
19807                             html : '&nbsp;&nbsp;&nbsp'
19808                         }
19809
19810                     ]
19811                 };
19812
19813                 var ctr = _this.el.select('.fc-event-container',true).first();
19814                 var cg = ctr.createChild(cfg);
19815
19816                 var sbox = c.select('.fc-day-content',true).first().getBox();
19817                 var ebox = c.select('.fc-day-content',true).first().getBox();
19818                 //Roo.log(cg);
19819                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19820                 cg.setWidth(ebox.right - sbox.x -2);
19821
19822                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19823                 
19824             }
19825             
19826         });
19827         
19828         
19829         
19830     },
19831     
19832     onEventEnter: function (e, el,event,d) {
19833         this.fireEvent('evententer', this, el, event);
19834     },
19835     
19836     onEventLeave: function (e, el,event,d) {
19837         this.fireEvent('eventleave', this, el, event);
19838     },
19839     
19840     onEventClick: function (e, el,event,d) {
19841         this.fireEvent('eventclick', this, el, event);
19842     },
19843     
19844     onMonthChange: function () {
19845         this.store.load();
19846     },
19847     
19848     onMoreEventClick: function(e, el, more)
19849     {
19850         var _this = this;
19851         
19852         this.calpopover.placement = 'right';
19853         this.calpopover.setTitle('More');
19854         
19855         this.calpopover.setContent('');
19856         
19857         var ctr = this.calpopover.el.select('.popover-content', true).first();
19858         
19859         Roo.each(more, function(m){
19860             var cfg = {
19861                 cls : 'fc-event-hori fc-event-draggable',
19862                 html : m.title
19863             };
19864             var cg = ctr.createChild(cfg);
19865             
19866             cg.on('click', _this.onEventClick, _this, m);
19867         });
19868         
19869         this.calpopover.show(el);
19870         
19871         
19872     },
19873     
19874     onLoad: function () 
19875     {   
19876         this.calevents = [];
19877         var cal = this;
19878         
19879         if(this.store.getCount() > 0){
19880             this.store.data.each(function(d){
19881                cal.addItem({
19882                     id : d.data.id,
19883                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19884                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19885                     time : d.data.start_time,
19886                     title : d.data.title,
19887                     description : d.data.description,
19888                     venue : d.data.venue
19889                 });
19890             });
19891         }
19892         
19893         this.renderEvents();
19894         
19895         if(this.calevents.length && this.loadMask){
19896             this.maskEl.hide();
19897         }
19898     },
19899     
19900     onBeforeLoad: function()
19901     {
19902         this.clearEvents();
19903         if(this.loadMask){
19904             this.maskEl.show();
19905         }
19906     }
19907 });
19908
19909  
19910  /*
19911  * - LGPL
19912  *
19913  * element
19914  * 
19915  */
19916
19917 /**
19918  * @class Roo.bootstrap.Popover
19919  * @extends Roo.bootstrap.Component
19920  * Bootstrap Popover class
19921  * @cfg {String} html contents of the popover   (or false to use children..)
19922  * @cfg {String} title of popover (or false to hide)
19923  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19924  * @cfg {String} trigger click || hover (or false to trigger manually)
19925  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19926  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19927  *      - if false and it has a 'parent' then it will be automatically added to that element
19928  *      - if string - Roo.get  will be called 
19929  * @cfg {Number} delay - delay before showing
19930  
19931  * @constructor
19932  * Create a new Popover
19933  * @param {Object} config The config object
19934  */
19935
19936 Roo.bootstrap.Popover = function(config){
19937     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19938     
19939     this.addEvents({
19940         // raw events
19941          /**
19942          * @event show
19943          * After the popover show
19944          * 
19945          * @param {Roo.bootstrap.Popover} this
19946          */
19947         "show" : true,
19948         /**
19949          * @event hide
19950          * After the popover hide
19951          * 
19952          * @param {Roo.bootstrap.Popover} this
19953          */
19954         "hide" : true
19955     });
19956 };
19957
19958 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19959     
19960     title: false,
19961     html: false,
19962     
19963     placement : 'right',
19964     trigger : 'hover', // hover
19965     modal : false,
19966     delay : 0,
19967     
19968     over: false,
19969     
19970     can_build_overlaid : false,
19971     
19972     maskEl : false, // the mask element
19973     headerEl : false,
19974     contentEl : false,
19975     alignEl : false, // when show is called with an element - this get's stored.
19976     
19977     getChildContainer : function()
19978     {
19979         return this.contentEl;
19980         
19981     },
19982     getPopoverHeader : function()
19983     {
19984         this.title = true; // flag not to hide it..
19985         this.headerEl.addClass('p-0');
19986         return this.headerEl
19987     },
19988     
19989     
19990     getAutoCreate : function(){
19991          
19992         var cfg = {
19993            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19994            style: 'display:block',
19995            cn : [
19996                 {
19997                     cls : 'arrow'
19998                 },
19999                 {
20000                     cls : 'popover-inner ',
20001                     cn : [
20002                         {
20003                             tag: 'h3',
20004                             cls: 'popover-title popover-header',
20005                             html : this.title === false ? '' : this.title
20006                         },
20007                         {
20008                             cls : 'popover-content popover-body '  + (this.cls || ''),
20009                             html : this.html || ''
20010                         }
20011                     ]
20012                     
20013                 }
20014            ]
20015         };
20016         
20017         return cfg;
20018     },
20019     /**
20020      * @param {string} the title
20021      */
20022     setTitle: function(str)
20023     {
20024         this.title = str;
20025         if (this.el) {
20026             this.headerEl.dom.innerHTML = str;
20027         }
20028         
20029     },
20030     /**
20031      * @param {string} the body content
20032      */
20033     setContent: function(str)
20034     {
20035         this.html = str;
20036         if (this.contentEl) {
20037             this.contentEl.dom.innerHTML = str;
20038         }
20039         
20040     },
20041     // as it get's added to the bottom of the page.
20042     onRender : function(ct, position)
20043     {
20044         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20045         
20046         
20047         
20048         if(!this.el){
20049             var cfg = Roo.apply({},  this.getAutoCreate());
20050             cfg.id = Roo.id();
20051             
20052             if (this.cls) {
20053                 cfg.cls += ' ' + this.cls;
20054             }
20055             if (this.style) {
20056                 cfg.style = this.style;
20057             }
20058             //Roo.log("adding to ");
20059             this.el = Roo.get(document.body).createChild(cfg, position);
20060 //            Roo.log(this.el);
20061         }
20062         
20063         this.contentEl = this.el.select('.popover-content',true).first();
20064         this.headerEl =  this.el.select('.popover-title',true).first();
20065         
20066         var nitems = [];
20067         if(typeof(this.items) != 'undefined'){
20068             var items = this.items;
20069             delete this.items;
20070
20071             for(var i =0;i < items.length;i++) {
20072                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20073             }
20074         }
20075
20076         this.items = nitems;
20077         
20078         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20079         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20080         
20081         
20082         
20083         this.initEvents();
20084     },
20085     
20086     resizeMask : function()
20087     {
20088         this.maskEl.setSize(
20089             Roo.lib.Dom.getViewWidth(true),
20090             Roo.lib.Dom.getViewHeight(true)
20091         );
20092     },
20093     
20094     initEvents : function()
20095     {
20096         
20097         if (!this.modal) { 
20098             Roo.bootstrap.Popover.register(this);
20099         }
20100          
20101         this.arrowEl = this.el.select('.arrow',true).first();
20102         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20103         this.el.enableDisplayMode('block');
20104         this.el.hide();
20105  
20106         
20107         if (this.over === false && !this.parent()) {
20108             return; 
20109         }
20110         if (this.triggers === false) {
20111             return;
20112         }
20113          
20114         // support parent
20115         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20116         var triggers = this.trigger ? this.trigger.split(' ') : [];
20117         Roo.each(triggers, function(trigger) {
20118         
20119             if (trigger == 'click') {
20120                 on_el.on('click', this.toggle, this);
20121             } else if (trigger != 'manual') {
20122                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20123                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20124       
20125                 on_el.on(eventIn  ,this.enter, this);
20126                 on_el.on(eventOut, this.leave, this);
20127             }
20128         }, this);
20129     },
20130     
20131     
20132     // private
20133     timeout : null,
20134     hoverState : null,
20135     
20136     toggle : function () {
20137         this.hoverState == 'in' ? this.leave() : this.enter();
20138     },
20139     
20140     enter : function () {
20141         
20142         clearTimeout(this.timeout);
20143     
20144         this.hoverState = 'in';
20145     
20146         if (!this.delay || !this.delay.show) {
20147             this.show();
20148             return;
20149         }
20150         var _t = this;
20151         this.timeout = setTimeout(function () {
20152             if (_t.hoverState == 'in') {
20153                 _t.show();
20154             }
20155         }, this.delay.show)
20156     },
20157     
20158     leave : function() {
20159         clearTimeout(this.timeout);
20160     
20161         this.hoverState = 'out';
20162     
20163         if (!this.delay || !this.delay.hide) {
20164             this.hide();
20165             return;
20166         }
20167         var _t = this;
20168         this.timeout = setTimeout(function () {
20169             if (_t.hoverState == 'out') {
20170                 _t.hide();
20171             }
20172         }, this.delay.hide)
20173     },
20174     /**
20175      * Show the popover
20176      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20177      * @param {string} (left|right|top|bottom) position
20178      */
20179     show : function (on_el, placement)
20180     {
20181         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20182         on_el = on_el || false; // default to false
20183          
20184         if (!on_el) {
20185             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20186                 on_el = this.parent().el;
20187             } else if (this.over) {
20188                 Roo.get(this.over);
20189             }
20190             
20191         }
20192         
20193         this.alignEl = Roo.get( on_el );
20194
20195         if (!this.el) {
20196             this.render(document.body);
20197         }
20198         
20199         
20200          
20201         
20202         if (this.title === false) {
20203             this.headerEl.hide();
20204         }
20205         
20206        
20207         this.el.show();
20208         this.el.dom.style.display = 'block';
20209          
20210  
20211         if (this.alignEl) {
20212             this.updatePosition(this.placement, true);
20213              
20214         } else {
20215             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20216             var es = this.el.getSize();
20217             var x = Roo.lib.Dom.getViewWidth()/2;
20218             var y = Roo.lib.Dom.getViewHeight()/2;
20219             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20220             
20221         }
20222
20223         
20224         //var arrow = this.el.select('.arrow',true).first();
20225         //arrow.set(align[2], 
20226         
20227         this.el.addClass('in');
20228         
20229          
20230         
20231         this.hoverState = 'in';
20232         
20233         if (this.modal) {
20234             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20235             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20236             this.maskEl.dom.style.display = 'block';
20237             this.maskEl.addClass('show');
20238         }
20239         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20240  
20241         this.fireEvent('show', this);
20242         
20243     },
20244     /**
20245      * fire this manually after loading a grid in the table for example
20246      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20247      * @param {Boolean} try and move it if we cant get right position.
20248      */
20249     updatePosition : function(placement, try_move)
20250     {
20251         // allow for calling with no parameters
20252         placement = placement   ? placement :  this.placement;
20253         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20254         
20255         this.el.removeClass([
20256             'fade','top','bottom', 'left', 'right','in',
20257             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20258         ]);
20259         this.el.addClass(placement + ' bs-popover-' + placement);
20260         
20261         if (!this.alignEl ) {
20262             return false;
20263         }
20264         
20265         switch (placement) {
20266             case 'right':
20267                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20268                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20269                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20270                     //normal display... or moved up/down.
20271                     this.el.setXY(offset);
20272                     var xy = this.alignEl.getAnchorXY('tr', false);
20273                     xy[0]+=2;xy[1]+=5;
20274                     this.arrowEl.setXY(xy);
20275                     return true;
20276                 }
20277                 // continue through...
20278                 return this.updatePosition('left', false);
20279                 
20280             
20281             case 'left':
20282                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20283                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20284                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20285                     //normal display... or moved up/down.
20286                     this.el.setXY(offset);
20287                     var xy = this.alignEl.getAnchorXY('tl', false);
20288                     xy[0]-=10;xy[1]+=5; // << fix me
20289                     this.arrowEl.setXY(xy);
20290                     return true;
20291                 }
20292                 // call self...
20293                 return this.updatePosition('right', false);
20294             
20295             case 'top':
20296                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20297                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20298                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20299                     //normal display... or moved up/down.
20300                     this.el.setXY(offset);
20301                     var xy = this.alignEl.getAnchorXY('t', false);
20302                     xy[1]-=10; // << fix me
20303                     this.arrowEl.setXY(xy);
20304                     return true;
20305                 }
20306                 // fall through
20307                return this.updatePosition('bottom', false);
20308             
20309             case 'bottom':
20310                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20311                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20312                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20313                     //normal display... or moved up/down.
20314                     this.el.setXY(offset);
20315                     var xy = this.alignEl.getAnchorXY('b', false);
20316                      xy[1]+=2; // << fix me
20317                     this.arrowEl.setXY(xy);
20318                     return true;
20319                 }
20320                 // fall through
20321                 return this.updatePosition('top', false);
20322                 
20323             
20324         }
20325         
20326         
20327         return false;
20328     },
20329     
20330     hide : function()
20331     {
20332         this.el.setXY([0,0]);
20333         this.el.removeClass('in');
20334         this.el.hide();
20335         this.hoverState = null;
20336         this.maskEl.hide(); // always..
20337         this.fireEvent('hide', this);
20338     }
20339     
20340 });
20341
20342
20343 Roo.apply(Roo.bootstrap.Popover, {
20344
20345     alignment : {
20346         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20347         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20348         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20349         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20350     },
20351     
20352     zIndex : 20001,
20353
20354     clickHander : false,
20355     
20356
20357     onMouseDown : function(e)
20358     {
20359         if (!e.getTarget(".roo-popover")) {
20360             this.hideAll();
20361         }
20362          
20363     },
20364     
20365     popups : [],
20366     
20367     register : function(popup)
20368     {
20369         if (!Roo.bootstrap.Popover.clickHandler) {
20370             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20371         }
20372         // hide other popups.
20373         this.hideAll();
20374         this.popups.push(popup);
20375     },
20376     hideAll : function()
20377     {
20378         this.popups.forEach(function(p) {
20379             p.hide();
20380         });
20381     }
20382
20383 });/*
20384  * - LGPL
20385  *
20386  * Card header - holder for the card header elements.
20387  * 
20388  */
20389
20390 /**
20391  * @class Roo.bootstrap.PopoverNav
20392  * @extends Roo.bootstrap.NavGroup
20393  * Bootstrap Popover header navigation class
20394  * @constructor
20395  * Create a new Popover Header Navigation 
20396  * @param {Object} config The config object
20397  */
20398
20399 Roo.bootstrap.PopoverNav = function(config){
20400     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20401 };
20402
20403 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20404     
20405     
20406     container_method : 'getPopoverHeader' 
20407     
20408      
20409     
20410     
20411    
20412 });
20413
20414  
20415
20416  /*
20417  * - LGPL
20418  *
20419  * Progress
20420  * 
20421  */
20422
20423 /**
20424  * @class Roo.bootstrap.Progress
20425  * @extends Roo.bootstrap.Component
20426  * Bootstrap Progress class
20427  * @cfg {Boolean} striped striped of the progress bar
20428  * @cfg {Boolean} active animated of the progress bar
20429  * 
20430  * 
20431  * @constructor
20432  * Create a new Progress
20433  * @param {Object} config The config object
20434  */
20435
20436 Roo.bootstrap.Progress = function(config){
20437     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20438 };
20439
20440 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20441     
20442     striped : false,
20443     active: false,
20444     
20445     getAutoCreate : function(){
20446         var cfg = {
20447             tag: 'div',
20448             cls: 'progress'
20449         };
20450         
20451         
20452         if(this.striped){
20453             cfg.cls += ' progress-striped';
20454         }
20455       
20456         if(this.active){
20457             cfg.cls += ' active';
20458         }
20459         
20460         
20461         return cfg;
20462     }
20463    
20464 });
20465
20466  
20467
20468  /*
20469  * - LGPL
20470  *
20471  * ProgressBar
20472  * 
20473  */
20474
20475 /**
20476  * @class Roo.bootstrap.ProgressBar
20477  * @extends Roo.bootstrap.Component
20478  * Bootstrap ProgressBar class
20479  * @cfg {Number} aria_valuenow aria-value now
20480  * @cfg {Number} aria_valuemin aria-value min
20481  * @cfg {Number} aria_valuemax aria-value max
20482  * @cfg {String} label label for the progress bar
20483  * @cfg {String} panel (success | info | warning | danger )
20484  * @cfg {String} role role of the progress bar
20485  * @cfg {String} sr_only text
20486  * 
20487  * 
20488  * @constructor
20489  * Create a new ProgressBar
20490  * @param {Object} config The config object
20491  */
20492
20493 Roo.bootstrap.ProgressBar = function(config){
20494     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20495 };
20496
20497 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20498     
20499     aria_valuenow : 0,
20500     aria_valuemin : 0,
20501     aria_valuemax : 100,
20502     label : false,
20503     panel : false,
20504     role : false,
20505     sr_only: false,
20506     
20507     getAutoCreate : function()
20508     {
20509         
20510         var cfg = {
20511             tag: 'div',
20512             cls: 'progress-bar',
20513             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20514         };
20515         
20516         if(this.sr_only){
20517             cfg.cn = {
20518                 tag: 'span',
20519                 cls: 'sr-only',
20520                 html: this.sr_only
20521             }
20522         }
20523         
20524         if(this.role){
20525             cfg.role = this.role;
20526         }
20527         
20528         if(this.aria_valuenow){
20529             cfg['aria-valuenow'] = this.aria_valuenow;
20530         }
20531         
20532         if(this.aria_valuemin){
20533             cfg['aria-valuemin'] = this.aria_valuemin;
20534         }
20535         
20536         if(this.aria_valuemax){
20537             cfg['aria-valuemax'] = this.aria_valuemax;
20538         }
20539         
20540         if(this.label && !this.sr_only){
20541             cfg.html = this.label;
20542         }
20543         
20544         if(this.panel){
20545             cfg.cls += ' progress-bar-' + this.panel;
20546         }
20547         
20548         return cfg;
20549     },
20550     
20551     update : function(aria_valuenow)
20552     {
20553         this.aria_valuenow = aria_valuenow;
20554         
20555         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20556     }
20557    
20558 });
20559
20560  
20561
20562  /*
20563  * - LGPL
20564  *
20565  * column
20566  * 
20567  */
20568
20569 /**
20570  * @class Roo.bootstrap.TabGroup
20571  * @extends Roo.bootstrap.Column
20572  * Bootstrap Column class
20573  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20574  * @cfg {Boolean} carousel true to make the group behave like a carousel
20575  * @cfg {Boolean} bullets show bullets for the panels
20576  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20577  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20578  * @cfg {Boolean} showarrow (true|false) show arrow default true
20579  * 
20580  * @constructor
20581  * Create a new TabGroup
20582  * @param {Object} config The config object
20583  */
20584
20585 Roo.bootstrap.TabGroup = function(config){
20586     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20587     if (!this.navId) {
20588         this.navId = Roo.id();
20589     }
20590     this.tabs = [];
20591     Roo.bootstrap.TabGroup.register(this);
20592     
20593 };
20594
20595 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20596     
20597     carousel : false,
20598     transition : false,
20599     bullets : 0,
20600     timer : 0,
20601     autoslide : false,
20602     slideFn : false,
20603     slideOnTouch : false,
20604     showarrow : true,
20605     
20606     getAutoCreate : function()
20607     {
20608         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20609         
20610         cfg.cls += ' tab-content';
20611         
20612         if (this.carousel) {
20613             cfg.cls += ' carousel slide';
20614             
20615             cfg.cn = [{
20616                cls : 'carousel-inner',
20617                cn : []
20618             }];
20619         
20620             if(this.bullets  && !Roo.isTouch){
20621                 
20622                 var bullets = {
20623                     cls : 'carousel-bullets',
20624                     cn : []
20625                 };
20626                
20627                 if(this.bullets_cls){
20628                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20629                 }
20630                 
20631                 bullets.cn.push({
20632                     cls : 'clear'
20633                 });
20634                 
20635                 cfg.cn[0].cn.push(bullets);
20636             }
20637             
20638             if(this.showarrow){
20639                 cfg.cn[0].cn.push({
20640                     tag : 'div',
20641                     class : 'carousel-arrow',
20642                     cn : [
20643                         {
20644                             tag : 'div',
20645                             class : 'carousel-prev',
20646                             cn : [
20647                                 {
20648                                     tag : 'i',
20649                                     class : 'fa fa-chevron-left'
20650                                 }
20651                             ]
20652                         },
20653                         {
20654                             tag : 'div',
20655                             class : 'carousel-next',
20656                             cn : [
20657                                 {
20658                                     tag : 'i',
20659                                     class : 'fa fa-chevron-right'
20660                                 }
20661                             ]
20662                         }
20663                     ]
20664                 });
20665             }
20666             
20667         }
20668         
20669         return cfg;
20670     },
20671     
20672     initEvents:  function()
20673     {
20674 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20675 //            this.el.on("touchstart", this.onTouchStart, this);
20676 //        }
20677         
20678         if(this.autoslide){
20679             var _this = this;
20680             
20681             this.slideFn = window.setInterval(function() {
20682                 _this.showPanelNext();
20683             }, this.timer);
20684         }
20685         
20686         if(this.showarrow){
20687             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20688             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20689         }
20690         
20691         
20692     },
20693     
20694 //    onTouchStart : function(e, el, o)
20695 //    {
20696 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20697 //            return;
20698 //        }
20699 //        
20700 //        this.showPanelNext();
20701 //    },
20702     
20703     
20704     getChildContainer : function()
20705     {
20706         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20707     },
20708     
20709     /**
20710     * register a Navigation item
20711     * @param {Roo.bootstrap.NavItem} the navitem to add
20712     */
20713     register : function(item)
20714     {
20715         this.tabs.push( item);
20716         item.navId = this.navId; // not really needed..
20717         this.addBullet();
20718     
20719     },
20720     
20721     getActivePanel : function()
20722     {
20723         var r = false;
20724         Roo.each(this.tabs, function(t) {
20725             if (t.active) {
20726                 r = t;
20727                 return false;
20728             }
20729             return null;
20730         });
20731         return r;
20732         
20733     },
20734     getPanelByName : function(n)
20735     {
20736         var r = false;
20737         Roo.each(this.tabs, function(t) {
20738             if (t.tabId == n) {
20739                 r = t;
20740                 return false;
20741             }
20742             return null;
20743         });
20744         return r;
20745     },
20746     indexOfPanel : function(p)
20747     {
20748         var r = false;
20749         Roo.each(this.tabs, function(t,i) {
20750             if (t.tabId == p.tabId) {
20751                 r = i;
20752                 return false;
20753             }
20754             return null;
20755         });
20756         return r;
20757     },
20758     /**
20759      * show a specific panel
20760      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20761      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20762      */
20763     showPanel : function (pan)
20764     {
20765         if(this.transition || typeof(pan) == 'undefined'){
20766             Roo.log("waiting for the transitionend");
20767             return false;
20768         }
20769         
20770         if (typeof(pan) == 'number') {
20771             pan = this.tabs[pan];
20772         }
20773         
20774         if (typeof(pan) == 'string') {
20775             pan = this.getPanelByName(pan);
20776         }
20777         
20778         var cur = this.getActivePanel();
20779         
20780         if(!pan || !cur){
20781             Roo.log('pan or acitve pan is undefined');
20782             return false;
20783         }
20784         
20785         if (pan.tabId == this.getActivePanel().tabId) {
20786             return true;
20787         }
20788         
20789         if (false === cur.fireEvent('beforedeactivate')) {
20790             return false;
20791         }
20792         
20793         if(this.bullets > 0 && !Roo.isTouch){
20794             this.setActiveBullet(this.indexOfPanel(pan));
20795         }
20796         
20797         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20798             
20799             //class="carousel-item carousel-item-next carousel-item-left"
20800             
20801             this.transition = true;
20802             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20803             var lr = dir == 'next' ? 'left' : 'right';
20804             pan.el.addClass(dir); // or prev
20805             pan.el.addClass('carousel-item-' + dir); // or prev
20806             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20807             cur.el.addClass(lr); // or right
20808             pan.el.addClass(lr);
20809             cur.el.addClass('carousel-item-' +lr); // or right
20810             pan.el.addClass('carousel-item-' +lr);
20811             
20812             
20813             var _this = this;
20814             cur.el.on('transitionend', function() {
20815                 Roo.log("trans end?");
20816                 
20817                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20818                 pan.setActive(true);
20819                 
20820                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20821                 cur.setActive(false);
20822                 
20823                 _this.transition = false;
20824                 
20825             }, this, { single:  true } );
20826             
20827             return true;
20828         }
20829         
20830         cur.setActive(false);
20831         pan.setActive(true);
20832         
20833         return true;
20834         
20835     },
20836     showPanelNext : function()
20837     {
20838         var i = this.indexOfPanel(this.getActivePanel());
20839         
20840         if (i >= this.tabs.length - 1 && !this.autoslide) {
20841             return;
20842         }
20843         
20844         if (i >= this.tabs.length - 1 && this.autoslide) {
20845             i = -1;
20846         }
20847         
20848         this.showPanel(this.tabs[i+1]);
20849     },
20850     
20851     showPanelPrev : function()
20852     {
20853         var i = this.indexOfPanel(this.getActivePanel());
20854         
20855         if (i  < 1 && !this.autoslide) {
20856             return;
20857         }
20858         
20859         if (i < 1 && this.autoslide) {
20860             i = this.tabs.length;
20861         }
20862         
20863         this.showPanel(this.tabs[i-1]);
20864     },
20865     
20866     
20867     addBullet: function()
20868     {
20869         if(!this.bullets || Roo.isTouch){
20870             return;
20871         }
20872         var ctr = this.el.select('.carousel-bullets',true).first();
20873         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20874         var bullet = ctr.createChild({
20875             cls : 'bullet bullet-' + i
20876         },ctr.dom.lastChild);
20877         
20878         
20879         var _this = this;
20880         
20881         bullet.on('click', (function(e, el, o, ii, t){
20882
20883             e.preventDefault();
20884
20885             this.showPanel(ii);
20886
20887             if(this.autoslide && this.slideFn){
20888                 clearInterval(this.slideFn);
20889                 this.slideFn = window.setInterval(function() {
20890                     _this.showPanelNext();
20891                 }, this.timer);
20892             }
20893
20894         }).createDelegate(this, [i, bullet], true));
20895                 
20896         
20897     },
20898      
20899     setActiveBullet : function(i)
20900     {
20901         if(Roo.isTouch){
20902             return;
20903         }
20904         
20905         Roo.each(this.el.select('.bullet', true).elements, function(el){
20906             el.removeClass('selected');
20907         });
20908
20909         var bullet = this.el.select('.bullet-' + i, true).first();
20910         
20911         if(!bullet){
20912             return;
20913         }
20914         
20915         bullet.addClass('selected');
20916     }
20917     
20918     
20919   
20920 });
20921
20922  
20923
20924  
20925  
20926 Roo.apply(Roo.bootstrap.TabGroup, {
20927     
20928     groups: {},
20929      /**
20930     * register a Navigation Group
20931     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20932     */
20933     register : function(navgrp)
20934     {
20935         this.groups[navgrp.navId] = navgrp;
20936         
20937     },
20938     /**
20939     * fetch a Navigation Group based on the navigation ID
20940     * if one does not exist , it will get created.
20941     * @param {string} the navgroup to add
20942     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20943     */
20944     get: function(navId) {
20945         if (typeof(this.groups[navId]) == 'undefined') {
20946             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20947         }
20948         return this.groups[navId] ;
20949     }
20950     
20951     
20952     
20953 });
20954
20955  /*
20956  * - LGPL
20957  *
20958  * TabPanel
20959  * 
20960  */
20961
20962 /**
20963  * @class Roo.bootstrap.TabPanel
20964  * @extends Roo.bootstrap.Component
20965  * Bootstrap TabPanel class
20966  * @cfg {Boolean} active panel active
20967  * @cfg {String} html panel content
20968  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20969  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20970  * @cfg {String} href click to link..
20971  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20972  * 
20973  * 
20974  * @constructor
20975  * Create a new TabPanel
20976  * @param {Object} config The config object
20977  */
20978
20979 Roo.bootstrap.TabPanel = function(config){
20980     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20981     this.addEvents({
20982         /**
20983              * @event changed
20984              * Fires when the active status changes
20985              * @param {Roo.bootstrap.TabPanel} this
20986              * @param {Boolean} state the new state
20987             
20988          */
20989         'changed': true,
20990         /**
20991              * @event beforedeactivate
20992              * Fires before a tab is de-activated - can be used to do validation on a form.
20993              * @param {Roo.bootstrap.TabPanel} this
20994              * @return {Boolean} false if there is an error
20995             
20996          */
20997         'beforedeactivate': true
20998      });
20999     
21000     this.tabId = this.tabId || Roo.id();
21001   
21002 };
21003
21004 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21005     
21006     active: false,
21007     html: false,
21008     tabId: false,
21009     navId : false,
21010     href : '',
21011     touchSlide : false,
21012     getAutoCreate : function(){
21013         
21014         
21015         var cfg = {
21016             tag: 'div',
21017             // item is needed for carousel - not sure if it has any effect otherwise
21018             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21019             html: this.html || ''
21020         };
21021         
21022         if(this.active){
21023             cfg.cls += ' active';
21024         }
21025         
21026         if(this.tabId){
21027             cfg.tabId = this.tabId;
21028         }
21029         
21030         
21031         
21032         return cfg;
21033     },
21034     
21035     initEvents:  function()
21036     {
21037         var p = this.parent();
21038         
21039         this.navId = this.navId || p.navId;
21040         
21041         if (typeof(this.navId) != 'undefined') {
21042             // not really needed.. but just in case.. parent should be a NavGroup.
21043             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21044             
21045             tg.register(this);
21046             
21047             var i = tg.tabs.length - 1;
21048             
21049             if(this.active && tg.bullets > 0 && i < tg.bullets){
21050                 tg.setActiveBullet(i);
21051             }
21052         }
21053         
21054         this.el.on('click', this.onClick, this);
21055         
21056         if(Roo.isTouch && this.touchSlide){
21057             this.el.on("touchstart", this.onTouchStart, this);
21058             this.el.on("touchmove", this.onTouchMove, this);
21059             this.el.on("touchend", this.onTouchEnd, this);
21060         }
21061         
21062     },
21063     
21064     onRender : function(ct, position)
21065     {
21066         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21067     },
21068     
21069     setActive : function(state)
21070     {
21071         Roo.log("panel - set active " + this.tabId + "=" + state);
21072         
21073         this.active = state;
21074         if (!state) {
21075             this.el.removeClass('active');
21076             
21077         } else  if (!this.el.hasClass('active')) {
21078             this.el.addClass('active');
21079         }
21080         
21081         this.fireEvent('changed', this, state);
21082     },
21083     
21084     onClick : function(e)
21085     {
21086         e.preventDefault();
21087         
21088         if(!this.href.length){
21089             return;
21090         }
21091         
21092         window.location.href = this.href;
21093     },
21094     
21095     startX : 0,
21096     startY : 0,
21097     endX : 0,
21098     endY : 0,
21099     swiping : false,
21100     
21101     onTouchStart : function(e)
21102     {
21103         this.swiping = false;
21104         
21105         this.startX = e.browserEvent.touches[0].clientX;
21106         this.startY = e.browserEvent.touches[0].clientY;
21107     },
21108     
21109     onTouchMove : function(e)
21110     {
21111         this.swiping = true;
21112         
21113         this.endX = e.browserEvent.touches[0].clientX;
21114         this.endY = e.browserEvent.touches[0].clientY;
21115     },
21116     
21117     onTouchEnd : function(e)
21118     {
21119         if(!this.swiping){
21120             this.onClick(e);
21121             return;
21122         }
21123         
21124         var tabGroup = this.parent();
21125         
21126         if(this.endX > this.startX){ // swiping right
21127             tabGroup.showPanelPrev();
21128             return;
21129         }
21130         
21131         if(this.startX > this.endX){ // swiping left
21132             tabGroup.showPanelNext();
21133             return;
21134         }
21135     }
21136     
21137     
21138 });
21139  
21140
21141  
21142
21143  /*
21144  * - LGPL
21145  *
21146  * DateField
21147  * 
21148  */
21149
21150 /**
21151  * @class Roo.bootstrap.DateField
21152  * @extends Roo.bootstrap.Input
21153  * Bootstrap DateField class
21154  * @cfg {Number} weekStart default 0
21155  * @cfg {String} viewMode default empty, (months|years)
21156  * @cfg {String} minViewMode default empty, (months|years)
21157  * @cfg {Number} startDate default -Infinity
21158  * @cfg {Number} endDate default Infinity
21159  * @cfg {Boolean} todayHighlight default false
21160  * @cfg {Boolean} todayBtn default false
21161  * @cfg {Boolean} calendarWeeks default false
21162  * @cfg {Object} daysOfWeekDisabled default empty
21163  * @cfg {Boolean} singleMode default false (true | false)
21164  * 
21165  * @cfg {Boolean} keyboardNavigation default true
21166  * @cfg {String} language default en
21167  * 
21168  * @constructor
21169  * Create a new DateField
21170  * @param {Object} config The config object
21171  */
21172
21173 Roo.bootstrap.DateField = function(config){
21174     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21175      this.addEvents({
21176             /**
21177              * @event show
21178              * Fires when this field show.
21179              * @param {Roo.bootstrap.DateField} this
21180              * @param {Mixed} date The date value
21181              */
21182             show : true,
21183             /**
21184              * @event show
21185              * Fires when this field hide.
21186              * @param {Roo.bootstrap.DateField} this
21187              * @param {Mixed} date The date value
21188              */
21189             hide : true,
21190             /**
21191              * @event select
21192              * Fires when select a date.
21193              * @param {Roo.bootstrap.DateField} this
21194              * @param {Mixed} date The date value
21195              */
21196             select : true,
21197             /**
21198              * @event beforeselect
21199              * Fires when before select a date.
21200              * @param {Roo.bootstrap.DateField} this
21201              * @param {Mixed} date The date value
21202              */
21203             beforeselect : true
21204         });
21205 };
21206
21207 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21208     
21209     /**
21210      * @cfg {String} format
21211      * The default date format string which can be overriden for localization support.  The format must be
21212      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21213      */
21214     format : "m/d/y",
21215     /**
21216      * @cfg {String} altFormats
21217      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21218      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21219      */
21220     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21221     
21222     weekStart : 0,
21223     
21224     viewMode : '',
21225     
21226     minViewMode : '',
21227     
21228     todayHighlight : false,
21229     
21230     todayBtn: false,
21231     
21232     language: 'en',
21233     
21234     keyboardNavigation: true,
21235     
21236     calendarWeeks: false,
21237     
21238     startDate: -Infinity,
21239     
21240     endDate: Infinity,
21241     
21242     daysOfWeekDisabled: [],
21243     
21244     _events: [],
21245     
21246     singleMode : false,
21247     
21248     UTCDate: function()
21249     {
21250         return new Date(Date.UTC.apply(Date, arguments));
21251     },
21252     
21253     UTCToday: function()
21254     {
21255         var today = new Date();
21256         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21257     },
21258     
21259     getDate: function() {
21260             var d = this.getUTCDate();
21261             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21262     },
21263     
21264     getUTCDate: function() {
21265             return this.date;
21266     },
21267     
21268     setDate: function(d) {
21269             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21270     },
21271     
21272     setUTCDate: function(d) {
21273             this.date = d;
21274             this.setValue(this.formatDate(this.date));
21275     },
21276         
21277     onRender: function(ct, position)
21278     {
21279         
21280         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21281         
21282         this.language = this.language || 'en';
21283         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21284         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21285         
21286         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21287         this.format = this.format || 'm/d/y';
21288         this.isInline = false;
21289         this.isInput = true;
21290         this.component = this.el.select('.add-on', true).first() || false;
21291         this.component = (this.component && this.component.length === 0) ? false : this.component;
21292         this.hasInput = this.component && this.inputEl().length;
21293         
21294         if (typeof(this.minViewMode === 'string')) {
21295             switch (this.minViewMode) {
21296                 case 'months':
21297                     this.minViewMode = 1;
21298                     break;
21299                 case 'years':
21300                     this.minViewMode = 2;
21301                     break;
21302                 default:
21303                     this.minViewMode = 0;
21304                     break;
21305             }
21306         }
21307         
21308         if (typeof(this.viewMode === 'string')) {
21309             switch (this.viewMode) {
21310                 case 'months':
21311                     this.viewMode = 1;
21312                     break;
21313                 case 'years':
21314                     this.viewMode = 2;
21315                     break;
21316                 default:
21317                     this.viewMode = 0;
21318                     break;
21319             }
21320         }
21321                 
21322         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21323         
21324 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21325         
21326         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21327         
21328         this.picker().on('mousedown', this.onMousedown, this);
21329         this.picker().on('click', this.onClick, this);
21330         
21331         this.picker().addClass('datepicker-dropdown');
21332         
21333         this.startViewMode = this.viewMode;
21334         
21335         if(this.singleMode){
21336             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21337                 v.setVisibilityMode(Roo.Element.DISPLAY);
21338                 v.hide();
21339             });
21340             
21341             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21342                 v.setStyle('width', '189px');
21343             });
21344         }
21345         
21346         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21347             if(!this.calendarWeeks){
21348                 v.remove();
21349                 return;
21350             }
21351             
21352             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21353             v.attr('colspan', function(i, val){
21354                 return parseInt(val) + 1;
21355             });
21356         });
21357                         
21358         
21359         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21360         
21361         this.setStartDate(this.startDate);
21362         this.setEndDate(this.endDate);
21363         
21364         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21365         
21366         this.fillDow();
21367         this.fillMonths();
21368         this.update();
21369         this.showMode();
21370         
21371         if(this.isInline) {
21372             this.showPopup();
21373         }
21374     },
21375     
21376     picker : function()
21377     {
21378         return this.pickerEl;
21379 //        return this.el.select('.datepicker', true).first();
21380     },
21381     
21382     fillDow: function()
21383     {
21384         var dowCnt = this.weekStart;
21385         
21386         var dow = {
21387             tag: 'tr',
21388             cn: [
21389                 
21390             ]
21391         };
21392         
21393         if(this.calendarWeeks){
21394             dow.cn.push({
21395                 tag: 'th',
21396                 cls: 'cw',
21397                 html: '&nbsp;'
21398             })
21399         }
21400         
21401         while (dowCnt < this.weekStart + 7) {
21402             dow.cn.push({
21403                 tag: 'th',
21404                 cls: 'dow',
21405                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21406             });
21407         }
21408         
21409         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21410     },
21411     
21412     fillMonths: function()
21413     {    
21414         var i = 0;
21415         var months = this.picker().select('>.datepicker-months td', true).first();
21416         
21417         months.dom.innerHTML = '';
21418         
21419         while (i < 12) {
21420             var month = {
21421                 tag: 'span',
21422                 cls: 'month',
21423                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21424             };
21425             
21426             months.createChild(month);
21427         }
21428         
21429     },
21430     
21431     update: function()
21432     {
21433         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;
21434         
21435         if (this.date < this.startDate) {
21436             this.viewDate = new Date(this.startDate);
21437         } else if (this.date > this.endDate) {
21438             this.viewDate = new Date(this.endDate);
21439         } else {
21440             this.viewDate = new Date(this.date);
21441         }
21442         
21443         this.fill();
21444     },
21445     
21446     fill: function() 
21447     {
21448         var d = new Date(this.viewDate),
21449                 year = d.getUTCFullYear(),
21450                 month = d.getUTCMonth(),
21451                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21452                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21453                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21454                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21455                 currentDate = this.date && this.date.valueOf(),
21456                 today = this.UTCToday();
21457         
21458         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21459         
21460 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21461         
21462 //        this.picker.select('>tfoot th.today').
21463 //                                              .text(dates[this.language].today)
21464 //                                              .toggle(this.todayBtn !== false);
21465     
21466         this.updateNavArrows();
21467         this.fillMonths();
21468                                                 
21469         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21470         
21471         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21472          
21473         prevMonth.setUTCDate(day);
21474         
21475         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21476         
21477         var nextMonth = new Date(prevMonth);
21478         
21479         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21480         
21481         nextMonth = nextMonth.valueOf();
21482         
21483         var fillMonths = false;
21484         
21485         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21486         
21487         while(prevMonth.valueOf() <= nextMonth) {
21488             var clsName = '';
21489             
21490             if (prevMonth.getUTCDay() === this.weekStart) {
21491                 if(fillMonths){
21492                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21493                 }
21494                     
21495                 fillMonths = {
21496                     tag: 'tr',
21497                     cn: []
21498                 };
21499                 
21500                 if(this.calendarWeeks){
21501                     // ISO 8601: First week contains first thursday.
21502                     // ISO also states week starts on Monday, but we can be more abstract here.
21503                     var
21504                     // Start of current week: based on weekstart/current date
21505                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21506                     // Thursday of this week
21507                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21508                     // First Thursday of year, year from thursday
21509                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21510                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21511                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21512                     
21513                     fillMonths.cn.push({
21514                         tag: 'td',
21515                         cls: 'cw',
21516                         html: calWeek
21517                     });
21518                 }
21519             }
21520             
21521             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21522                 clsName += ' old';
21523             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21524                 clsName += ' new';
21525             }
21526             if (this.todayHighlight &&
21527                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21528                 prevMonth.getUTCMonth() == today.getMonth() &&
21529                 prevMonth.getUTCDate() == today.getDate()) {
21530                 clsName += ' today';
21531             }
21532             
21533             if (currentDate && prevMonth.valueOf() === currentDate) {
21534                 clsName += ' active';
21535             }
21536             
21537             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21538                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21539                     clsName += ' disabled';
21540             }
21541             
21542             fillMonths.cn.push({
21543                 tag: 'td',
21544                 cls: 'day ' + clsName,
21545                 html: prevMonth.getDate()
21546             });
21547             
21548             prevMonth.setDate(prevMonth.getDate()+1);
21549         }
21550           
21551         var currentYear = this.date && this.date.getUTCFullYear();
21552         var currentMonth = this.date && this.date.getUTCMonth();
21553         
21554         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21555         
21556         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21557             v.removeClass('active');
21558             
21559             if(currentYear === year && k === currentMonth){
21560                 v.addClass('active');
21561             }
21562             
21563             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21564                 v.addClass('disabled');
21565             }
21566             
21567         });
21568         
21569         
21570         year = parseInt(year/10, 10) * 10;
21571         
21572         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21573         
21574         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21575         
21576         year -= 1;
21577         for (var i = -1; i < 11; i++) {
21578             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21579                 tag: 'span',
21580                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21581                 html: year
21582             });
21583             
21584             year += 1;
21585         }
21586     },
21587     
21588     showMode: function(dir) 
21589     {
21590         if (dir) {
21591             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21592         }
21593         
21594         Roo.each(this.picker().select('>div',true).elements, function(v){
21595             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21596             v.hide();
21597         });
21598         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21599     },
21600     
21601     place: function()
21602     {
21603         if(this.isInline) {
21604             return;
21605         }
21606         
21607         this.picker().removeClass(['bottom', 'top']);
21608         
21609         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21610             /*
21611              * place to the top of element!
21612              *
21613              */
21614             
21615             this.picker().addClass('top');
21616             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21617             
21618             return;
21619         }
21620         
21621         this.picker().addClass('bottom');
21622         
21623         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21624     },
21625     
21626     parseDate : function(value)
21627     {
21628         if(!value || value instanceof Date){
21629             return value;
21630         }
21631         var v = Date.parseDate(value, this.format);
21632         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21633             v = Date.parseDate(value, 'Y-m-d');
21634         }
21635         if(!v && this.altFormats){
21636             if(!this.altFormatsArray){
21637                 this.altFormatsArray = this.altFormats.split("|");
21638             }
21639             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21640                 v = Date.parseDate(value, this.altFormatsArray[i]);
21641             }
21642         }
21643         return v;
21644     },
21645     
21646     formatDate : function(date, fmt)
21647     {   
21648         return (!date || !(date instanceof Date)) ?
21649         date : date.dateFormat(fmt || this.format);
21650     },
21651     
21652     onFocus : function()
21653     {
21654         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21655         this.showPopup();
21656     },
21657     
21658     onBlur : function()
21659     {
21660         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21661         
21662         var d = this.inputEl().getValue();
21663         
21664         this.setValue(d);
21665                 
21666         this.hidePopup();
21667     },
21668     
21669     showPopup : function()
21670     {
21671         this.picker().show();
21672         this.update();
21673         this.place();
21674         
21675         this.fireEvent('showpopup', this, this.date);
21676     },
21677     
21678     hidePopup : function()
21679     {
21680         if(this.isInline) {
21681             return;
21682         }
21683         this.picker().hide();
21684         this.viewMode = this.startViewMode;
21685         this.showMode();
21686         
21687         this.fireEvent('hidepopup', this, this.date);
21688         
21689     },
21690     
21691     onMousedown: function(e)
21692     {
21693         e.stopPropagation();
21694         e.preventDefault();
21695     },
21696     
21697     keyup: function(e)
21698     {
21699         Roo.bootstrap.DateField.superclass.keyup.call(this);
21700         this.update();
21701     },
21702
21703     setValue: function(v)
21704     {
21705         if(this.fireEvent('beforeselect', this, v) !== false){
21706             var d = new Date(this.parseDate(v) ).clearTime();
21707         
21708             if(isNaN(d.getTime())){
21709                 this.date = this.viewDate = '';
21710                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21711                 return;
21712             }
21713
21714             v = this.formatDate(d);
21715
21716             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21717
21718             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21719
21720             this.update();
21721
21722             this.fireEvent('select', this, this.date);
21723         }
21724     },
21725     
21726     getValue: function()
21727     {
21728         return this.formatDate(this.date);
21729     },
21730     
21731     fireKey: function(e)
21732     {
21733         if (!this.picker().isVisible()){
21734             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21735                 this.showPopup();
21736             }
21737             return;
21738         }
21739         
21740         var dateChanged = false,
21741         dir, day, month,
21742         newDate, newViewDate;
21743         
21744         switch(e.keyCode){
21745             case 27: // escape
21746                 this.hidePopup();
21747                 e.preventDefault();
21748                 break;
21749             case 37: // left
21750             case 39: // right
21751                 if (!this.keyboardNavigation) {
21752                     break;
21753                 }
21754                 dir = e.keyCode == 37 ? -1 : 1;
21755                 
21756                 if (e.ctrlKey){
21757                     newDate = this.moveYear(this.date, dir);
21758                     newViewDate = this.moveYear(this.viewDate, dir);
21759                 } else if (e.shiftKey){
21760                     newDate = this.moveMonth(this.date, dir);
21761                     newViewDate = this.moveMonth(this.viewDate, dir);
21762                 } else {
21763                     newDate = new Date(this.date);
21764                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21765                     newViewDate = new Date(this.viewDate);
21766                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21767                 }
21768                 if (this.dateWithinRange(newDate)){
21769                     this.date = newDate;
21770                     this.viewDate = newViewDate;
21771                     this.setValue(this.formatDate(this.date));
21772 //                    this.update();
21773                     e.preventDefault();
21774                     dateChanged = true;
21775                 }
21776                 break;
21777             case 38: // up
21778             case 40: // down
21779                 if (!this.keyboardNavigation) {
21780                     break;
21781                 }
21782                 dir = e.keyCode == 38 ? -1 : 1;
21783                 if (e.ctrlKey){
21784                     newDate = this.moveYear(this.date, dir);
21785                     newViewDate = this.moveYear(this.viewDate, dir);
21786                 } else if (e.shiftKey){
21787                     newDate = this.moveMonth(this.date, dir);
21788                     newViewDate = this.moveMonth(this.viewDate, dir);
21789                 } else {
21790                     newDate = new Date(this.date);
21791                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21792                     newViewDate = new Date(this.viewDate);
21793                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21794                 }
21795                 if (this.dateWithinRange(newDate)){
21796                     this.date = newDate;
21797                     this.viewDate = newViewDate;
21798                     this.setValue(this.formatDate(this.date));
21799 //                    this.update();
21800                     e.preventDefault();
21801                     dateChanged = true;
21802                 }
21803                 break;
21804             case 13: // enter
21805                 this.setValue(this.formatDate(this.date));
21806                 this.hidePopup();
21807                 e.preventDefault();
21808                 break;
21809             case 9: // tab
21810                 this.setValue(this.formatDate(this.date));
21811                 this.hidePopup();
21812                 break;
21813             case 16: // shift
21814             case 17: // ctrl
21815             case 18: // alt
21816                 break;
21817             default :
21818                 this.hidePopup();
21819                 
21820         }
21821     },
21822     
21823     
21824     onClick: function(e) 
21825     {
21826         e.stopPropagation();
21827         e.preventDefault();
21828         
21829         var target = e.getTarget();
21830         
21831         if(target.nodeName.toLowerCase() === 'i'){
21832             target = Roo.get(target).dom.parentNode;
21833         }
21834         
21835         var nodeName = target.nodeName;
21836         var className = target.className;
21837         var html = target.innerHTML;
21838         //Roo.log(nodeName);
21839         
21840         switch(nodeName.toLowerCase()) {
21841             case 'th':
21842                 switch(className) {
21843                     case 'switch':
21844                         this.showMode(1);
21845                         break;
21846                     case 'prev':
21847                     case 'next':
21848                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21849                         switch(this.viewMode){
21850                                 case 0:
21851                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21852                                         break;
21853                                 case 1:
21854                                 case 2:
21855                                         this.viewDate = this.moveYear(this.viewDate, dir);
21856                                         break;
21857                         }
21858                         this.fill();
21859                         break;
21860                     case 'today':
21861                         var date = new Date();
21862                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21863 //                        this.fill()
21864                         this.setValue(this.formatDate(this.date));
21865                         
21866                         this.hidePopup();
21867                         break;
21868                 }
21869                 break;
21870             case 'span':
21871                 if (className.indexOf('disabled') < 0) {
21872                     this.viewDate.setUTCDate(1);
21873                     if (className.indexOf('month') > -1) {
21874                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21875                     } else {
21876                         var year = parseInt(html, 10) || 0;
21877                         this.viewDate.setUTCFullYear(year);
21878                         
21879                     }
21880                     
21881                     if(this.singleMode){
21882                         this.setValue(this.formatDate(this.viewDate));
21883                         this.hidePopup();
21884                         return;
21885                     }
21886                     
21887                     this.showMode(-1);
21888                     this.fill();
21889                 }
21890                 break;
21891                 
21892             case 'td':
21893                 //Roo.log(className);
21894                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21895                     var day = parseInt(html, 10) || 1;
21896                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21897                         month = (this.viewDate || new Date()).getUTCMonth();
21898
21899                     if (className.indexOf('old') > -1) {
21900                         if(month === 0 ){
21901                             month = 11;
21902                             year -= 1;
21903                         }else{
21904                             month -= 1;
21905                         }
21906                     } else if (className.indexOf('new') > -1) {
21907                         if (month == 11) {
21908                             month = 0;
21909                             year += 1;
21910                         } else {
21911                             month += 1;
21912                         }
21913                     }
21914                     //Roo.log([year,month,day]);
21915                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21916                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21917 //                    this.fill();
21918                     //Roo.log(this.formatDate(this.date));
21919                     this.setValue(this.formatDate(this.date));
21920                     this.hidePopup();
21921                 }
21922                 break;
21923         }
21924     },
21925     
21926     setStartDate: function(startDate)
21927     {
21928         this.startDate = startDate || -Infinity;
21929         if (this.startDate !== -Infinity) {
21930             this.startDate = this.parseDate(this.startDate);
21931         }
21932         this.update();
21933         this.updateNavArrows();
21934     },
21935
21936     setEndDate: function(endDate)
21937     {
21938         this.endDate = endDate || Infinity;
21939         if (this.endDate !== Infinity) {
21940             this.endDate = this.parseDate(this.endDate);
21941         }
21942         this.update();
21943         this.updateNavArrows();
21944     },
21945     
21946     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21947     {
21948         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21949         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21950             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21951         }
21952         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21953             return parseInt(d, 10);
21954         });
21955         this.update();
21956         this.updateNavArrows();
21957     },
21958     
21959     updateNavArrows: function() 
21960     {
21961         if(this.singleMode){
21962             return;
21963         }
21964         
21965         var d = new Date(this.viewDate),
21966         year = d.getUTCFullYear(),
21967         month = d.getUTCMonth();
21968         
21969         Roo.each(this.picker().select('.prev', true).elements, function(v){
21970             v.show();
21971             switch (this.viewMode) {
21972                 case 0:
21973
21974                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21975                         v.hide();
21976                     }
21977                     break;
21978                 case 1:
21979                 case 2:
21980                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21981                         v.hide();
21982                     }
21983                     break;
21984             }
21985         });
21986         
21987         Roo.each(this.picker().select('.next', true).elements, function(v){
21988             v.show();
21989             switch (this.viewMode) {
21990                 case 0:
21991
21992                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21993                         v.hide();
21994                     }
21995                     break;
21996                 case 1:
21997                 case 2:
21998                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21999                         v.hide();
22000                     }
22001                     break;
22002             }
22003         })
22004     },
22005     
22006     moveMonth: function(date, dir)
22007     {
22008         if (!dir) {
22009             return date;
22010         }
22011         var new_date = new Date(date.valueOf()),
22012         day = new_date.getUTCDate(),
22013         month = new_date.getUTCMonth(),
22014         mag = Math.abs(dir),
22015         new_month, test;
22016         dir = dir > 0 ? 1 : -1;
22017         if (mag == 1){
22018             test = dir == -1
22019             // If going back one month, make sure month is not current month
22020             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22021             ? function(){
22022                 return new_date.getUTCMonth() == month;
22023             }
22024             // If going forward one month, make sure month is as expected
22025             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22026             : function(){
22027                 return new_date.getUTCMonth() != new_month;
22028             };
22029             new_month = month + dir;
22030             new_date.setUTCMonth(new_month);
22031             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22032             if (new_month < 0 || new_month > 11) {
22033                 new_month = (new_month + 12) % 12;
22034             }
22035         } else {
22036             // For magnitudes >1, move one month at a time...
22037             for (var i=0; i<mag; i++) {
22038                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22039                 new_date = this.moveMonth(new_date, dir);
22040             }
22041             // ...then reset the day, keeping it in the new month
22042             new_month = new_date.getUTCMonth();
22043             new_date.setUTCDate(day);
22044             test = function(){
22045                 return new_month != new_date.getUTCMonth();
22046             };
22047         }
22048         // Common date-resetting loop -- if date is beyond end of month, make it
22049         // end of month
22050         while (test()){
22051             new_date.setUTCDate(--day);
22052             new_date.setUTCMonth(new_month);
22053         }
22054         return new_date;
22055     },
22056
22057     moveYear: function(date, dir)
22058     {
22059         return this.moveMonth(date, dir*12);
22060     },
22061
22062     dateWithinRange: function(date)
22063     {
22064         return date >= this.startDate && date <= this.endDate;
22065     },
22066
22067     
22068     remove: function() 
22069     {
22070         this.picker().remove();
22071     },
22072     
22073     validateValue : function(value)
22074     {
22075         if(this.getVisibilityEl().hasClass('hidden')){
22076             return true;
22077         }
22078         
22079         if(value.length < 1)  {
22080             if(this.allowBlank){
22081                 return true;
22082             }
22083             return false;
22084         }
22085         
22086         if(value.length < this.minLength){
22087             return false;
22088         }
22089         if(value.length > this.maxLength){
22090             return false;
22091         }
22092         if(this.vtype){
22093             var vt = Roo.form.VTypes;
22094             if(!vt[this.vtype](value, this)){
22095                 return false;
22096             }
22097         }
22098         if(typeof this.validator == "function"){
22099             var msg = this.validator(value);
22100             if(msg !== true){
22101                 return false;
22102             }
22103         }
22104         
22105         if(this.regex && !this.regex.test(value)){
22106             return false;
22107         }
22108         
22109         if(typeof(this.parseDate(value)) == 'undefined'){
22110             return false;
22111         }
22112         
22113         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22114             return false;
22115         }      
22116         
22117         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22118             return false;
22119         } 
22120         
22121         
22122         return true;
22123     },
22124     
22125     reset : function()
22126     {
22127         this.date = this.viewDate = '';
22128         
22129         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22130     }
22131    
22132 });
22133
22134 Roo.apply(Roo.bootstrap.DateField,  {
22135     
22136     head : {
22137         tag: 'thead',
22138         cn: [
22139         {
22140             tag: 'tr',
22141             cn: [
22142             {
22143                 tag: 'th',
22144                 cls: 'prev',
22145                 html: '<i class="fa fa-arrow-left"/>'
22146             },
22147             {
22148                 tag: 'th',
22149                 cls: 'switch',
22150                 colspan: '5'
22151             },
22152             {
22153                 tag: 'th',
22154                 cls: 'next',
22155                 html: '<i class="fa fa-arrow-right"/>'
22156             }
22157
22158             ]
22159         }
22160         ]
22161     },
22162     
22163     content : {
22164         tag: 'tbody',
22165         cn: [
22166         {
22167             tag: 'tr',
22168             cn: [
22169             {
22170                 tag: 'td',
22171                 colspan: '7'
22172             }
22173             ]
22174         }
22175         ]
22176     },
22177     
22178     footer : {
22179         tag: 'tfoot',
22180         cn: [
22181         {
22182             tag: 'tr',
22183             cn: [
22184             {
22185                 tag: 'th',
22186                 colspan: '7',
22187                 cls: 'today'
22188             }
22189                     
22190             ]
22191         }
22192         ]
22193     },
22194     
22195     dates:{
22196         en: {
22197             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22198             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22199             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22200             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22201             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22202             today: "Today"
22203         }
22204     },
22205     
22206     modes: [
22207     {
22208         clsName: 'days',
22209         navFnc: 'Month',
22210         navStep: 1
22211     },
22212     {
22213         clsName: 'months',
22214         navFnc: 'FullYear',
22215         navStep: 1
22216     },
22217     {
22218         clsName: 'years',
22219         navFnc: 'FullYear',
22220         navStep: 10
22221     }]
22222 });
22223
22224 Roo.apply(Roo.bootstrap.DateField,  {
22225   
22226     template : {
22227         tag: 'div',
22228         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22229         cn: [
22230         {
22231             tag: 'div',
22232             cls: 'datepicker-days',
22233             cn: [
22234             {
22235                 tag: 'table',
22236                 cls: 'table-condensed',
22237                 cn:[
22238                 Roo.bootstrap.DateField.head,
22239                 {
22240                     tag: 'tbody'
22241                 },
22242                 Roo.bootstrap.DateField.footer
22243                 ]
22244             }
22245             ]
22246         },
22247         {
22248             tag: 'div',
22249             cls: 'datepicker-months',
22250             cn: [
22251             {
22252                 tag: 'table',
22253                 cls: 'table-condensed',
22254                 cn:[
22255                 Roo.bootstrap.DateField.head,
22256                 Roo.bootstrap.DateField.content,
22257                 Roo.bootstrap.DateField.footer
22258                 ]
22259             }
22260             ]
22261         },
22262         {
22263             tag: 'div',
22264             cls: 'datepicker-years',
22265             cn: [
22266             {
22267                 tag: 'table',
22268                 cls: 'table-condensed',
22269                 cn:[
22270                 Roo.bootstrap.DateField.head,
22271                 Roo.bootstrap.DateField.content,
22272                 Roo.bootstrap.DateField.footer
22273                 ]
22274             }
22275             ]
22276         }
22277         ]
22278     }
22279 });
22280
22281  
22282
22283  /*
22284  * - LGPL
22285  *
22286  * TimeField
22287  * 
22288  */
22289
22290 /**
22291  * @class Roo.bootstrap.TimeField
22292  * @extends Roo.bootstrap.Input
22293  * Bootstrap DateField class
22294  * 
22295  * 
22296  * @constructor
22297  * Create a new TimeField
22298  * @param {Object} config The config object
22299  */
22300
22301 Roo.bootstrap.TimeField = function(config){
22302     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22303     this.addEvents({
22304             /**
22305              * @event show
22306              * Fires when this field show.
22307              * @param {Roo.bootstrap.DateField} thisthis
22308              * @param {Mixed} date The date value
22309              */
22310             show : true,
22311             /**
22312              * @event show
22313              * Fires when this field hide.
22314              * @param {Roo.bootstrap.DateField} this
22315              * @param {Mixed} date The date value
22316              */
22317             hide : true,
22318             /**
22319              * @event select
22320              * Fires when select a date.
22321              * @param {Roo.bootstrap.DateField} this
22322              * @param {Mixed} date The date value
22323              */
22324             select : true
22325         });
22326 };
22327
22328 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22329     
22330     /**
22331      * @cfg {String} format
22332      * The default time format string which can be overriden for localization support.  The format must be
22333      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22334      */
22335     format : "H:i",
22336
22337     getAutoCreate : function()
22338     {
22339         this.after = '<i class="fa far fa-clock"></i>';
22340         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22341         
22342          
22343     },
22344     onRender: function(ct, position)
22345     {
22346         
22347         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22348                 
22349         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22350         
22351         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22352         
22353         this.pop = this.picker().select('>.datepicker-time',true).first();
22354         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22355         
22356         this.picker().on('mousedown', this.onMousedown, this);
22357         this.picker().on('click', this.onClick, this);
22358         
22359         this.picker().addClass('datepicker-dropdown');
22360     
22361         this.fillTime();
22362         this.update();
22363             
22364         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22365         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22366         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22367         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22368         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22369         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22370
22371     },
22372     
22373     fireKey: function(e){
22374         if (!this.picker().isVisible()){
22375             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22376                 this.show();
22377             }
22378             return;
22379         }
22380
22381         e.preventDefault();
22382         
22383         switch(e.keyCode){
22384             case 27: // escape
22385                 this.hide();
22386                 break;
22387             case 37: // left
22388             case 39: // right
22389                 this.onTogglePeriod();
22390                 break;
22391             case 38: // up
22392                 this.onIncrementMinutes();
22393                 break;
22394             case 40: // down
22395                 this.onDecrementMinutes();
22396                 break;
22397             case 13: // enter
22398             case 9: // tab
22399                 this.setTime();
22400                 break;
22401         }
22402     },
22403     
22404     onClick: function(e) {
22405         e.stopPropagation();
22406         e.preventDefault();
22407     },
22408     
22409     picker : function()
22410     {
22411         return this.pickerEl;
22412     },
22413     
22414     fillTime: function()
22415     {    
22416         var time = this.pop.select('tbody', true).first();
22417         
22418         time.dom.innerHTML = '';
22419         
22420         time.createChild({
22421             tag: 'tr',
22422             cn: [
22423                 {
22424                     tag: 'td',
22425                     cn: [
22426                         {
22427                             tag: 'a',
22428                             href: '#',
22429                             cls: 'btn',
22430                             cn: [
22431                                 {
22432                                     tag: 'i',
22433                                     cls: 'hours-up fa fas fa-chevron-up'
22434                                 }
22435                             ]
22436                         } 
22437                     ]
22438                 },
22439                 {
22440                     tag: 'td',
22441                     cls: 'separator'
22442                 },
22443                 {
22444                     tag: 'td',
22445                     cn: [
22446                         {
22447                             tag: 'a',
22448                             href: '#',
22449                             cls: 'btn',
22450                             cn: [
22451                                 {
22452                                     tag: 'i',
22453                                     cls: 'minutes-up fa fas fa-chevron-up'
22454                                 }
22455                             ]
22456                         }
22457                     ]
22458                 },
22459                 {
22460                     tag: 'td',
22461                     cls: 'separator'
22462                 }
22463             ]
22464         });
22465         
22466         time.createChild({
22467             tag: 'tr',
22468             cn: [
22469                 {
22470                     tag: 'td',
22471                     cn: [
22472                         {
22473                             tag: 'span',
22474                             cls: 'timepicker-hour',
22475                             html: '00'
22476                         }  
22477                     ]
22478                 },
22479                 {
22480                     tag: 'td',
22481                     cls: 'separator',
22482                     html: ':'
22483                 },
22484                 {
22485                     tag: 'td',
22486                     cn: [
22487                         {
22488                             tag: 'span',
22489                             cls: 'timepicker-minute',
22490                             html: '00'
22491                         }  
22492                     ]
22493                 },
22494                 {
22495                     tag: 'td',
22496                     cls: 'separator'
22497                 },
22498                 {
22499                     tag: 'td',
22500                     cn: [
22501                         {
22502                             tag: 'button',
22503                             type: 'button',
22504                             cls: 'btn btn-primary period',
22505                             html: 'AM'
22506                             
22507                         }
22508                     ]
22509                 }
22510             ]
22511         });
22512         
22513         time.createChild({
22514             tag: 'tr',
22515             cn: [
22516                 {
22517                     tag: 'td',
22518                     cn: [
22519                         {
22520                             tag: 'a',
22521                             href: '#',
22522                             cls: 'btn',
22523                             cn: [
22524                                 {
22525                                     tag: 'span',
22526                                     cls: 'hours-down fa fas fa-chevron-down'
22527                                 }
22528                             ]
22529                         }
22530                     ]
22531                 },
22532                 {
22533                     tag: 'td',
22534                     cls: 'separator'
22535                 },
22536                 {
22537                     tag: 'td',
22538                     cn: [
22539                         {
22540                             tag: 'a',
22541                             href: '#',
22542                             cls: 'btn',
22543                             cn: [
22544                                 {
22545                                     tag: 'span',
22546                                     cls: 'minutes-down fa fas fa-chevron-down'
22547                                 }
22548                             ]
22549                         }
22550                     ]
22551                 },
22552                 {
22553                     tag: 'td',
22554                     cls: 'separator'
22555                 }
22556             ]
22557         });
22558         
22559     },
22560     
22561     update: function()
22562     {
22563         
22564         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22565         
22566         this.fill();
22567     },
22568     
22569     fill: function() 
22570     {
22571         var hours = this.time.getHours();
22572         var minutes = this.time.getMinutes();
22573         var period = 'AM';
22574         
22575         if(hours > 11){
22576             period = 'PM';
22577         }
22578         
22579         if(hours == 0){
22580             hours = 12;
22581         }
22582         
22583         
22584         if(hours > 12){
22585             hours = hours - 12;
22586         }
22587         
22588         if(hours < 10){
22589             hours = '0' + hours;
22590         }
22591         
22592         if(minutes < 10){
22593             minutes = '0' + minutes;
22594         }
22595         
22596         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22597         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22598         this.pop.select('button', true).first().dom.innerHTML = period;
22599         
22600     },
22601     
22602     place: function()
22603     {   
22604         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22605         
22606         var cls = ['bottom'];
22607         
22608         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22609             cls.pop();
22610             cls.push('top');
22611         }
22612         
22613         cls.push('right');
22614         
22615         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22616             cls.pop();
22617             cls.push('left');
22618         }
22619         //this.picker().setXY(20000,20000);
22620         this.picker().addClass(cls.join('-'));
22621         
22622         var _this = this;
22623         
22624         Roo.each(cls, function(c){
22625             if(c == 'bottom'){
22626                 (function() {
22627                  //  
22628                 }).defer(200);
22629                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22630                 //_this.picker().setTop(_this.inputEl().getHeight());
22631                 return;
22632             }
22633             if(c == 'top'){
22634                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22635                 
22636                 //_this.picker().setTop(0 - _this.picker().getHeight());
22637                 return;
22638             }
22639             /*
22640             if(c == 'left'){
22641                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22642                 return;
22643             }
22644             if(c == 'right'){
22645                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22646                 return;
22647             }
22648             */
22649         });
22650         
22651     },
22652   
22653     onFocus : function()
22654     {
22655         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22656         this.show();
22657     },
22658     
22659     onBlur : function()
22660     {
22661         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22662         this.hide();
22663     },
22664     
22665     show : function()
22666     {
22667         this.picker().show();
22668         this.pop.show();
22669         this.update();
22670         this.place();
22671         
22672         this.fireEvent('show', this, this.date);
22673     },
22674     
22675     hide : function()
22676     {
22677         this.picker().hide();
22678         this.pop.hide();
22679         
22680         this.fireEvent('hide', this, this.date);
22681     },
22682     
22683     setTime : function()
22684     {
22685         this.hide();
22686         this.setValue(this.time.format(this.format));
22687         
22688         this.fireEvent('select', this, this.date);
22689         
22690         
22691     },
22692     
22693     onMousedown: function(e){
22694         e.stopPropagation();
22695         e.preventDefault();
22696     },
22697     
22698     onIncrementHours: function()
22699     {
22700         Roo.log('onIncrementHours');
22701         this.time = this.time.add(Date.HOUR, 1);
22702         this.update();
22703         
22704     },
22705     
22706     onDecrementHours: function()
22707     {
22708         Roo.log('onDecrementHours');
22709         this.time = this.time.add(Date.HOUR, -1);
22710         this.update();
22711     },
22712     
22713     onIncrementMinutes: function()
22714     {
22715         Roo.log('onIncrementMinutes');
22716         this.time = this.time.add(Date.MINUTE, 1);
22717         this.update();
22718     },
22719     
22720     onDecrementMinutes: function()
22721     {
22722         Roo.log('onDecrementMinutes');
22723         this.time = this.time.add(Date.MINUTE, -1);
22724         this.update();
22725     },
22726     
22727     onTogglePeriod: function()
22728     {
22729         Roo.log('onTogglePeriod');
22730         this.time = this.time.add(Date.HOUR, 12);
22731         this.update();
22732     }
22733     
22734    
22735 });
22736  
22737
22738 Roo.apply(Roo.bootstrap.TimeField,  {
22739   
22740     template : {
22741         tag: 'div',
22742         cls: 'datepicker dropdown-menu',
22743         cn: [
22744             {
22745                 tag: 'div',
22746                 cls: 'datepicker-time',
22747                 cn: [
22748                 {
22749                     tag: 'table',
22750                     cls: 'table-condensed',
22751                     cn:[
22752                         {
22753                             tag: 'tbody',
22754                             cn: [
22755                                 {
22756                                     tag: 'tr',
22757                                     cn: [
22758                                     {
22759                                         tag: 'td',
22760                                         colspan: '7'
22761                                     }
22762                                     ]
22763                                 }
22764                             ]
22765                         },
22766                         {
22767                             tag: 'tfoot',
22768                             cn: [
22769                                 {
22770                                     tag: 'tr',
22771                                     cn: [
22772                                     {
22773                                         tag: 'th',
22774                                         colspan: '7',
22775                                         cls: '',
22776                                         cn: [
22777                                             {
22778                                                 tag: 'button',
22779                                                 cls: 'btn btn-info ok',
22780                                                 html: 'OK'
22781                                             }
22782                                         ]
22783                                     }
22784                     
22785                                     ]
22786                                 }
22787                             ]
22788                         }
22789                     ]
22790                 }
22791                 ]
22792             }
22793         ]
22794     }
22795 });
22796
22797  
22798
22799  /*
22800  * - LGPL
22801  *
22802  * MonthField
22803  * 
22804  */
22805
22806 /**
22807  * @class Roo.bootstrap.MonthField
22808  * @extends Roo.bootstrap.Input
22809  * Bootstrap MonthField class
22810  * 
22811  * @cfg {String} language default en
22812  * 
22813  * @constructor
22814  * Create a new MonthField
22815  * @param {Object} config The config object
22816  */
22817
22818 Roo.bootstrap.MonthField = function(config){
22819     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22820     
22821     this.addEvents({
22822         /**
22823          * @event show
22824          * Fires when this field show.
22825          * @param {Roo.bootstrap.MonthField} this
22826          * @param {Mixed} date The date value
22827          */
22828         show : true,
22829         /**
22830          * @event show
22831          * Fires when this field hide.
22832          * @param {Roo.bootstrap.MonthField} this
22833          * @param {Mixed} date The date value
22834          */
22835         hide : true,
22836         /**
22837          * @event select
22838          * Fires when select a date.
22839          * @param {Roo.bootstrap.MonthField} this
22840          * @param {String} oldvalue The old value
22841          * @param {String} newvalue The new value
22842          */
22843         select : true
22844     });
22845 };
22846
22847 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22848     
22849     onRender: function(ct, position)
22850     {
22851         
22852         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22853         
22854         this.language = this.language || 'en';
22855         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22856         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22857         
22858         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22859         this.isInline = false;
22860         this.isInput = true;
22861         this.component = this.el.select('.add-on', true).first() || false;
22862         this.component = (this.component && this.component.length === 0) ? false : this.component;
22863         this.hasInput = this.component && this.inputEL().length;
22864         
22865         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22866         
22867         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22868         
22869         this.picker().on('mousedown', this.onMousedown, this);
22870         this.picker().on('click', this.onClick, this);
22871         
22872         this.picker().addClass('datepicker-dropdown');
22873         
22874         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22875             v.setStyle('width', '189px');
22876         });
22877         
22878         this.fillMonths();
22879         
22880         this.update();
22881         
22882         if(this.isInline) {
22883             this.show();
22884         }
22885         
22886     },
22887     
22888     setValue: function(v, suppressEvent)
22889     {   
22890         var o = this.getValue();
22891         
22892         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22893         
22894         this.update();
22895
22896         if(suppressEvent !== true){
22897             this.fireEvent('select', this, o, v);
22898         }
22899         
22900     },
22901     
22902     getValue: function()
22903     {
22904         return this.value;
22905     },
22906     
22907     onClick: function(e) 
22908     {
22909         e.stopPropagation();
22910         e.preventDefault();
22911         
22912         var target = e.getTarget();
22913         
22914         if(target.nodeName.toLowerCase() === 'i'){
22915             target = Roo.get(target).dom.parentNode;
22916         }
22917         
22918         var nodeName = target.nodeName;
22919         var className = target.className;
22920         var html = target.innerHTML;
22921         
22922         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22923             return;
22924         }
22925         
22926         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22927         
22928         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22929         
22930         this.hide();
22931                         
22932     },
22933     
22934     picker : function()
22935     {
22936         return this.pickerEl;
22937     },
22938     
22939     fillMonths: function()
22940     {    
22941         var i = 0;
22942         var months = this.picker().select('>.datepicker-months td', true).first();
22943         
22944         months.dom.innerHTML = '';
22945         
22946         while (i < 12) {
22947             var month = {
22948                 tag: 'span',
22949                 cls: 'month',
22950                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22951             };
22952             
22953             months.createChild(month);
22954         }
22955         
22956     },
22957     
22958     update: function()
22959     {
22960         var _this = this;
22961         
22962         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22963             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22964         }
22965         
22966         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22967             e.removeClass('active');
22968             
22969             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22970                 e.addClass('active');
22971             }
22972         })
22973     },
22974     
22975     place: function()
22976     {
22977         if(this.isInline) {
22978             return;
22979         }
22980         
22981         this.picker().removeClass(['bottom', 'top']);
22982         
22983         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22984             /*
22985              * place to the top of element!
22986              *
22987              */
22988             
22989             this.picker().addClass('top');
22990             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22991             
22992             return;
22993         }
22994         
22995         this.picker().addClass('bottom');
22996         
22997         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22998     },
22999     
23000     onFocus : function()
23001     {
23002         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23003         this.show();
23004     },
23005     
23006     onBlur : function()
23007     {
23008         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23009         
23010         var d = this.inputEl().getValue();
23011         
23012         this.setValue(d);
23013                 
23014         this.hide();
23015     },
23016     
23017     show : function()
23018     {
23019         this.picker().show();
23020         this.picker().select('>.datepicker-months', true).first().show();
23021         this.update();
23022         this.place();
23023         
23024         this.fireEvent('show', this, this.date);
23025     },
23026     
23027     hide : function()
23028     {
23029         if(this.isInline) {
23030             return;
23031         }
23032         this.picker().hide();
23033         this.fireEvent('hide', this, this.date);
23034         
23035     },
23036     
23037     onMousedown: function(e)
23038     {
23039         e.stopPropagation();
23040         e.preventDefault();
23041     },
23042     
23043     keyup: function(e)
23044     {
23045         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23046         this.update();
23047     },
23048
23049     fireKey: function(e)
23050     {
23051         if (!this.picker().isVisible()){
23052             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23053                 this.show();
23054             }
23055             return;
23056         }
23057         
23058         var dir;
23059         
23060         switch(e.keyCode){
23061             case 27: // escape
23062                 this.hide();
23063                 e.preventDefault();
23064                 break;
23065             case 37: // left
23066             case 39: // right
23067                 dir = e.keyCode == 37 ? -1 : 1;
23068                 
23069                 this.vIndex = this.vIndex + dir;
23070                 
23071                 if(this.vIndex < 0){
23072                     this.vIndex = 0;
23073                 }
23074                 
23075                 if(this.vIndex > 11){
23076                     this.vIndex = 11;
23077                 }
23078                 
23079                 if(isNaN(this.vIndex)){
23080                     this.vIndex = 0;
23081                 }
23082                 
23083                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23084                 
23085                 break;
23086             case 38: // up
23087             case 40: // down
23088                 
23089                 dir = e.keyCode == 38 ? -1 : 1;
23090                 
23091                 this.vIndex = this.vIndex + dir * 4;
23092                 
23093                 if(this.vIndex < 0){
23094                     this.vIndex = 0;
23095                 }
23096                 
23097                 if(this.vIndex > 11){
23098                     this.vIndex = 11;
23099                 }
23100                 
23101                 if(isNaN(this.vIndex)){
23102                     this.vIndex = 0;
23103                 }
23104                 
23105                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23106                 break;
23107                 
23108             case 13: // enter
23109                 
23110                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23111                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23112                 }
23113                 
23114                 this.hide();
23115                 e.preventDefault();
23116                 break;
23117             case 9: // tab
23118                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23119                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23120                 }
23121                 this.hide();
23122                 break;
23123             case 16: // shift
23124             case 17: // ctrl
23125             case 18: // alt
23126                 break;
23127             default :
23128                 this.hide();
23129                 
23130         }
23131     },
23132     
23133     remove: function() 
23134     {
23135         this.picker().remove();
23136     }
23137    
23138 });
23139
23140 Roo.apply(Roo.bootstrap.MonthField,  {
23141     
23142     content : {
23143         tag: 'tbody',
23144         cn: [
23145         {
23146             tag: 'tr',
23147             cn: [
23148             {
23149                 tag: 'td',
23150                 colspan: '7'
23151             }
23152             ]
23153         }
23154         ]
23155     },
23156     
23157     dates:{
23158         en: {
23159             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23160             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23161         }
23162     }
23163 });
23164
23165 Roo.apply(Roo.bootstrap.MonthField,  {
23166   
23167     template : {
23168         tag: 'div',
23169         cls: 'datepicker dropdown-menu roo-dynamic',
23170         cn: [
23171             {
23172                 tag: 'div',
23173                 cls: 'datepicker-months',
23174                 cn: [
23175                 {
23176                     tag: 'table',
23177                     cls: 'table-condensed',
23178                     cn:[
23179                         Roo.bootstrap.DateField.content
23180                     ]
23181                 }
23182                 ]
23183             }
23184         ]
23185     }
23186 });
23187
23188  
23189
23190  
23191  /*
23192  * - LGPL
23193  *
23194  * CheckBox
23195  * 
23196  */
23197
23198 /**
23199  * @class Roo.bootstrap.CheckBox
23200  * @extends Roo.bootstrap.Input
23201  * Bootstrap CheckBox class
23202  * 
23203  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23204  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23205  * @cfg {String} boxLabel The text that appears beside the checkbox
23206  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23207  * @cfg {Boolean} checked initnal the element
23208  * @cfg {Boolean} inline inline the element (default false)
23209  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23210  * @cfg {String} tooltip label tooltip
23211  * 
23212  * @constructor
23213  * Create a new CheckBox
23214  * @param {Object} config The config object
23215  */
23216
23217 Roo.bootstrap.CheckBox = function(config){
23218     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23219    
23220     this.addEvents({
23221         /**
23222         * @event check
23223         * Fires when the element is checked or unchecked.
23224         * @param {Roo.bootstrap.CheckBox} this This input
23225         * @param {Boolean} checked The new checked value
23226         */
23227        check : true,
23228        /**
23229         * @event click
23230         * Fires when the element is click.
23231         * @param {Roo.bootstrap.CheckBox} this This input
23232         */
23233        click : true
23234     });
23235     
23236 };
23237
23238 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23239   
23240     inputType: 'checkbox',
23241     inputValue: 1,
23242     valueOff: 0,
23243     boxLabel: false,
23244     checked: false,
23245     weight : false,
23246     inline: false,
23247     tooltip : '',
23248     
23249     // checkbox success does not make any sense really.. 
23250     invalidClass : "",
23251     validClass : "",
23252     
23253     
23254     getAutoCreate : function()
23255     {
23256         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23257         
23258         var id = Roo.id();
23259         
23260         var cfg = {};
23261         
23262         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23263         
23264         if(this.inline){
23265             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23266         }
23267         
23268         var input =  {
23269             tag: 'input',
23270             id : id,
23271             type : this.inputType,
23272             value : this.inputValue,
23273             cls : 'roo-' + this.inputType, //'form-box',
23274             placeholder : this.placeholder || ''
23275             
23276         };
23277         
23278         if(this.inputType != 'radio'){
23279             var hidden =  {
23280                 tag: 'input',
23281                 type : 'hidden',
23282                 cls : 'roo-hidden-value',
23283                 value : this.checked ? this.inputValue : this.valueOff
23284             };
23285         }
23286         
23287             
23288         if (this.weight) { // Validity check?
23289             cfg.cls += " " + this.inputType + "-" + this.weight;
23290         }
23291         
23292         if (this.disabled) {
23293             input.disabled=true;
23294         }
23295         
23296         if(this.checked){
23297             input.checked = this.checked;
23298         }
23299         
23300         if (this.name) {
23301             
23302             input.name = this.name;
23303             
23304             if(this.inputType != 'radio'){
23305                 hidden.name = this.name;
23306                 input.name = '_hidden_' + this.name;
23307             }
23308         }
23309         
23310         if (this.size) {
23311             input.cls += ' input-' + this.size;
23312         }
23313         
23314         var settings=this;
23315         
23316         ['xs','sm','md','lg'].map(function(size){
23317             if (settings[size]) {
23318                 cfg.cls += ' col-' + size + '-' + settings[size];
23319             }
23320         });
23321         
23322         var inputblock = input;
23323          
23324         if (this.before || this.after) {
23325             
23326             inputblock = {
23327                 cls : 'input-group',
23328                 cn :  [] 
23329             };
23330             
23331             if (this.before) {
23332                 inputblock.cn.push({
23333                     tag :'span',
23334                     cls : 'input-group-addon',
23335                     html : this.before
23336                 });
23337             }
23338             
23339             inputblock.cn.push(input);
23340             
23341             if(this.inputType != 'radio'){
23342                 inputblock.cn.push(hidden);
23343             }
23344             
23345             if (this.after) {
23346                 inputblock.cn.push({
23347                     tag :'span',
23348                     cls : 'input-group-addon',
23349                     html : this.after
23350                 });
23351             }
23352             
23353         }
23354         var boxLabelCfg = false;
23355         
23356         if(this.boxLabel){
23357            
23358             boxLabelCfg = {
23359                 tag: 'label',
23360                 //'for': id, // box label is handled by onclick - so no for...
23361                 cls: 'box-label',
23362                 html: this.boxLabel
23363             };
23364             if(this.tooltip){
23365                 boxLabelCfg.tooltip = this.tooltip;
23366             }
23367              
23368         }
23369         
23370         
23371         if (align ==='left' && this.fieldLabel.length) {
23372 //                Roo.log("left and has label");
23373             cfg.cn = [
23374                 {
23375                     tag: 'label',
23376                     'for' :  id,
23377                     cls : 'control-label',
23378                     html : this.fieldLabel
23379                 },
23380                 {
23381                     cls : "", 
23382                     cn: [
23383                         inputblock
23384                     ]
23385                 }
23386             ];
23387             
23388             if (boxLabelCfg) {
23389                 cfg.cn[1].cn.push(boxLabelCfg);
23390             }
23391             
23392             if(this.labelWidth > 12){
23393                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23394             }
23395             
23396             if(this.labelWidth < 13 && this.labelmd == 0){
23397                 this.labelmd = this.labelWidth;
23398             }
23399             
23400             if(this.labellg > 0){
23401                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23402                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23403             }
23404             
23405             if(this.labelmd > 0){
23406                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23407                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23408             }
23409             
23410             if(this.labelsm > 0){
23411                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23412                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23413             }
23414             
23415             if(this.labelxs > 0){
23416                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23417                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23418             }
23419             
23420         } else if ( this.fieldLabel.length) {
23421 //                Roo.log(" label");
23422                 cfg.cn = [
23423                    
23424                     {
23425                         tag: this.boxLabel ? 'span' : 'label',
23426                         'for': id,
23427                         cls: 'control-label box-input-label',
23428                         //cls : 'input-group-addon',
23429                         html : this.fieldLabel
23430                     },
23431                     
23432                     inputblock
23433                     
23434                 ];
23435                 if (boxLabelCfg) {
23436                     cfg.cn.push(boxLabelCfg);
23437                 }
23438
23439         } else {
23440             
23441 //                Roo.log(" no label && no align");
23442                 cfg.cn = [  inputblock ] ;
23443                 if (boxLabelCfg) {
23444                     cfg.cn.push(boxLabelCfg);
23445                 }
23446
23447                 
23448         }
23449         
23450        
23451         
23452         if(this.inputType != 'radio'){
23453             cfg.cn.push(hidden);
23454         }
23455         
23456         return cfg;
23457         
23458     },
23459     
23460     /**
23461      * return the real input element.
23462      */
23463     inputEl: function ()
23464     {
23465         return this.el.select('input.roo-' + this.inputType,true).first();
23466     },
23467     hiddenEl: function ()
23468     {
23469         return this.el.select('input.roo-hidden-value',true).first();
23470     },
23471     
23472     labelEl: function()
23473     {
23474         return this.el.select('label.control-label',true).first();
23475     },
23476     /* depricated... */
23477     
23478     label: function()
23479     {
23480         return this.labelEl();
23481     },
23482     
23483     boxLabelEl: function()
23484     {
23485         return this.el.select('label.box-label',true).first();
23486     },
23487     
23488     initEvents : function()
23489     {
23490 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23491         
23492         this.inputEl().on('click', this.onClick,  this);
23493         
23494         if (this.boxLabel) { 
23495             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23496         }
23497         
23498         this.startValue = this.getValue();
23499         
23500         if(this.groupId){
23501             Roo.bootstrap.CheckBox.register(this);
23502         }
23503     },
23504     
23505     onClick : function(e)
23506     {   
23507         if(this.fireEvent('click', this, e) !== false){
23508             this.setChecked(!this.checked);
23509         }
23510         
23511     },
23512     
23513     setChecked : function(state,suppressEvent)
23514     {
23515         this.startValue = this.getValue();
23516
23517         if(this.inputType == 'radio'){
23518             
23519             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23520                 e.dom.checked = false;
23521             });
23522             
23523             this.inputEl().dom.checked = true;
23524             
23525             this.inputEl().dom.value = this.inputValue;
23526             
23527             if(suppressEvent !== true){
23528                 this.fireEvent('check', this, true);
23529             }
23530             
23531             this.validate();
23532             
23533             return;
23534         }
23535         
23536         this.checked = state;
23537         
23538         this.inputEl().dom.checked = state;
23539         
23540         
23541         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23542         
23543         if(suppressEvent !== true){
23544             this.fireEvent('check', this, state);
23545         }
23546         
23547         this.validate();
23548     },
23549     
23550     getValue : function()
23551     {
23552         if(this.inputType == 'radio'){
23553             return this.getGroupValue();
23554         }
23555         
23556         return this.hiddenEl().dom.value;
23557         
23558     },
23559     
23560     getGroupValue : function()
23561     {
23562         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23563             return '';
23564         }
23565         
23566         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23567     },
23568     
23569     setValue : function(v,suppressEvent)
23570     {
23571         if(this.inputType == 'radio'){
23572             this.setGroupValue(v, suppressEvent);
23573             return;
23574         }
23575         
23576         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23577         
23578         this.validate();
23579     },
23580     
23581     setGroupValue : function(v, suppressEvent)
23582     {
23583         this.startValue = this.getValue();
23584         
23585         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23586             e.dom.checked = false;
23587             
23588             if(e.dom.value == v){
23589                 e.dom.checked = true;
23590             }
23591         });
23592         
23593         if(suppressEvent !== true){
23594             this.fireEvent('check', this, true);
23595         }
23596
23597         this.validate();
23598         
23599         return;
23600     },
23601     
23602     validate : function()
23603     {
23604         if(this.getVisibilityEl().hasClass('hidden')){
23605             return true;
23606         }
23607         
23608         if(
23609                 this.disabled || 
23610                 (this.inputType == 'radio' && this.validateRadio()) ||
23611                 (this.inputType == 'checkbox' && this.validateCheckbox())
23612         ){
23613             this.markValid();
23614             return true;
23615         }
23616         
23617         this.markInvalid();
23618         return false;
23619     },
23620     
23621     validateRadio : function()
23622     {
23623         if(this.getVisibilityEl().hasClass('hidden')){
23624             return true;
23625         }
23626         
23627         if(this.allowBlank){
23628             return true;
23629         }
23630         
23631         var valid = false;
23632         
23633         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23634             if(!e.dom.checked){
23635                 return;
23636             }
23637             
23638             valid = true;
23639             
23640             return false;
23641         });
23642         
23643         return valid;
23644     },
23645     
23646     validateCheckbox : function()
23647     {
23648         if(!this.groupId){
23649             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23650             //return (this.getValue() == this.inputValue) ? true : false;
23651         }
23652         
23653         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23654         
23655         if(!group){
23656             return false;
23657         }
23658         
23659         var r = false;
23660         
23661         for(var i in group){
23662             if(group[i].el.isVisible(true)){
23663                 r = false;
23664                 break;
23665             }
23666             
23667             r = true;
23668         }
23669         
23670         for(var i in group){
23671             if(r){
23672                 break;
23673             }
23674             
23675             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23676         }
23677         
23678         return r;
23679     },
23680     
23681     /**
23682      * Mark this field as valid
23683      */
23684     markValid : function()
23685     {
23686         var _this = this;
23687         
23688         this.fireEvent('valid', this);
23689         
23690         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23691         
23692         if(this.groupId){
23693             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23694         }
23695         
23696         if(label){
23697             label.markValid();
23698         }
23699
23700         if(this.inputType == 'radio'){
23701             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23702                 var fg = e.findParent('.form-group', false, true);
23703                 if (Roo.bootstrap.version == 3) {
23704                     fg.removeClass([_this.invalidClass, _this.validClass]);
23705                     fg.addClass(_this.validClass);
23706                 } else {
23707                     fg.removeClass(['is-valid', 'is-invalid']);
23708                     fg.addClass('is-valid');
23709                 }
23710             });
23711             
23712             return;
23713         }
23714
23715         if(!this.groupId){
23716             var fg = this.el.findParent('.form-group', false, true);
23717             if (Roo.bootstrap.version == 3) {
23718                 fg.removeClass([this.invalidClass, this.validClass]);
23719                 fg.addClass(this.validClass);
23720             } else {
23721                 fg.removeClass(['is-valid', 'is-invalid']);
23722                 fg.addClass('is-valid');
23723             }
23724             return;
23725         }
23726         
23727         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23728         
23729         if(!group){
23730             return;
23731         }
23732         
23733         for(var i in group){
23734             var fg = group[i].el.findParent('.form-group', false, true);
23735             if (Roo.bootstrap.version == 3) {
23736                 fg.removeClass([this.invalidClass, this.validClass]);
23737                 fg.addClass(this.validClass);
23738             } else {
23739                 fg.removeClass(['is-valid', 'is-invalid']);
23740                 fg.addClass('is-valid');
23741             }
23742         }
23743     },
23744     
23745      /**
23746      * Mark this field as invalid
23747      * @param {String} msg The validation message
23748      */
23749     markInvalid : function(msg)
23750     {
23751         if(this.allowBlank){
23752             return;
23753         }
23754         
23755         var _this = this;
23756         
23757         this.fireEvent('invalid', this, msg);
23758         
23759         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23760         
23761         if(this.groupId){
23762             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23763         }
23764         
23765         if(label){
23766             label.markInvalid();
23767         }
23768             
23769         if(this.inputType == 'radio'){
23770             
23771             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23772                 var fg = e.findParent('.form-group', false, true);
23773                 if (Roo.bootstrap.version == 3) {
23774                     fg.removeClass([_this.invalidClass, _this.validClass]);
23775                     fg.addClass(_this.invalidClass);
23776                 } else {
23777                     fg.removeClass(['is-invalid', 'is-valid']);
23778                     fg.addClass('is-invalid');
23779                 }
23780             });
23781             
23782             return;
23783         }
23784         
23785         if(!this.groupId){
23786             var fg = this.el.findParent('.form-group', false, true);
23787             if (Roo.bootstrap.version == 3) {
23788                 fg.removeClass([_this.invalidClass, _this.validClass]);
23789                 fg.addClass(_this.invalidClass);
23790             } else {
23791                 fg.removeClass(['is-invalid', 'is-valid']);
23792                 fg.addClass('is-invalid');
23793             }
23794             return;
23795         }
23796         
23797         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23798         
23799         if(!group){
23800             return;
23801         }
23802         
23803         for(var i in group){
23804             var fg = group[i].el.findParent('.form-group', false, true);
23805             if (Roo.bootstrap.version == 3) {
23806                 fg.removeClass([_this.invalidClass, _this.validClass]);
23807                 fg.addClass(_this.invalidClass);
23808             } else {
23809                 fg.removeClass(['is-invalid', 'is-valid']);
23810                 fg.addClass('is-invalid');
23811             }
23812         }
23813         
23814     },
23815     
23816     clearInvalid : function()
23817     {
23818         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23819         
23820         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23821         
23822         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23823         
23824         if (label && label.iconEl) {
23825             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23826             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23827         }
23828     },
23829     
23830     disable : function()
23831     {
23832         if(this.inputType != 'radio'){
23833             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23834             return;
23835         }
23836         
23837         var _this = this;
23838         
23839         if(this.rendered){
23840             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23841                 _this.getActionEl().addClass(this.disabledClass);
23842                 e.dom.disabled = true;
23843             });
23844         }
23845         
23846         this.disabled = true;
23847         this.fireEvent("disable", this);
23848         return this;
23849     },
23850
23851     enable : function()
23852     {
23853         if(this.inputType != 'radio'){
23854             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23855             return;
23856         }
23857         
23858         var _this = this;
23859         
23860         if(this.rendered){
23861             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23862                 _this.getActionEl().removeClass(this.disabledClass);
23863                 e.dom.disabled = false;
23864             });
23865         }
23866         
23867         this.disabled = false;
23868         this.fireEvent("enable", this);
23869         return this;
23870     },
23871     
23872     setBoxLabel : function(v)
23873     {
23874         this.boxLabel = v;
23875         
23876         if(this.rendered){
23877             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23878         }
23879     }
23880
23881 });
23882
23883 Roo.apply(Roo.bootstrap.CheckBox, {
23884     
23885     groups: {},
23886     
23887      /**
23888     * register a CheckBox Group
23889     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23890     */
23891     register : function(checkbox)
23892     {
23893         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23894             this.groups[checkbox.groupId] = {};
23895         }
23896         
23897         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23898             return;
23899         }
23900         
23901         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23902         
23903     },
23904     /**
23905     * fetch a CheckBox Group based on the group ID
23906     * @param {string} the group ID
23907     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23908     */
23909     get: function(groupId) {
23910         if (typeof(this.groups[groupId]) == 'undefined') {
23911             return false;
23912         }
23913         
23914         return this.groups[groupId] ;
23915     }
23916     
23917     
23918 });
23919 /*
23920  * - LGPL
23921  *
23922  * RadioItem
23923  * 
23924  */
23925
23926 /**
23927  * @class Roo.bootstrap.Radio
23928  * @extends Roo.bootstrap.Component
23929  * Bootstrap Radio class
23930  * @cfg {String} boxLabel - the label associated
23931  * @cfg {String} value - the value of radio
23932  * 
23933  * @constructor
23934  * Create a new Radio
23935  * @param {Object} config The config object
23936  */
23937 Roo.bootstrap.Radio = function(config){
23938     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23939     
23940 };
23941
23942 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23943     
23944     boxLabel : '',
23945     
23946     value : '',
23947     
23948     getAutoCreate : function()
23949     {
23950         var cfg = {
23951             tag : 'div',
23952             cls : 'form-group radio',
23953             cn : [
23954                 {
23955                     tag : 'label',
23956                     cls : 'box-label',
23957                     html : this.boxLabel
23958                 }
23959             ]
23960         };
23961         
23962         return cfg;
23963     },
23964     
23965     initEvents : function() 
23966     {
23967         this.parent().register(this);
23968         
23969         this.el.on('click', this.onClick, this);
23970         
23971     },
23972     
23973     onClick : function(e)
23974     {
23975         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23976             this.setChecked(true);
23977         }
23978     },
23979     
23980     setChecked : function(state, suppressEvent)
23981     {
23982         this.parent().setValue(this.value, suppressEvent);
23983         
23984     },
23985     
23986     setBoxLabel : function(v)
23987     {
23988         this.boxLabel = v;
23989         
23990         if(this.rendered){
23991             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23992         }
23993     }
23994     
23995 });
23996  
23997
23998  /*
23999  * - LGPL
24000  *
24001  * Input
24002  * 
24003  */
24004
24005 /**
24006  * @class Roo.bootstrap.SecurePass
24007  * @extends Roo.bootstrap.Input
24008  * Bootstrap SecurePass class
24009  *
24010  * 
24011  * @constructor
24012  * Create a new SecurePass
24013  * @param {Object} config The config object
24014  */
24015  
24016 Roo.bootstrap.SecurePass = function (config) {
24017     // these go here, so the translation tool can replace them..
24018     this.errors = {
24019         PwdEmpty: "Please type a password, and then retype it to confirm.",
24020         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24021         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24022         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24023         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24024         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24025         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24026         TooWeak: "Your password is Too Weak."
24027     },
24028     this.meterLabel = "Password strength:";
24029     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24030     this.meterClass = [
24031         "roo-password-meter-tooweak", 
24032         "roo-password-meter-weak", 
24033         "roo-password-meter-medium", 
24034         "roo-password-meter-strong", 
24035         "roo-password-meter-grey"
24036     ];
24037     
24038     this.errors = {};
24039     
24040     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24041 }
24042
24043 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24044     /**
24045      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24046      * {
24047      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24048      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24049      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24050      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24051      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24052      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24053      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24054      * })
24055      */
24056     // private
24057     
24058     meterWidth: 300,
24059     errorMsg :'',    
24060     errors: false,
24061     imageRoot: '/',
24062     /**
24063      * @cfg {String/Object} Label for the strength meter (defaults to
24064      * 'Password strength:')
24065      */
24066     // private
24067     meterLabel: '',
24068     /**
24069      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24070      * ['Weak', 'Medium', 'Strong'])
24071      */
24072     // private    
24073     pwdStrengths: false,    
24074     // private
24075     strength: 0,
24076     // private
24077     _lastPwd: null,
24078     // private
24079     kCapitalLetter: 0,
24080     kSmallLetter: 1,
24081     kDigit: 2,
24082     kPunctuation: 3,
24083     
24084     insecure: false,
24085     // private
24086     initEvents: function ()
24087     {
24088         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24089
24090         if (this.el.is('input[type=password]') && Roo.isSafari) {
24091             this.el.on('keydown', this.SafariOnKeyDown, this);
24092         }
24093
24094         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24095     },
24096     // private
24097     onRender: function (ct, position)
24098     {
24099         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24100         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24101         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24102
24103         this.trigger.createChild({
24104                    cn: [
24105                     {
24106                     //id: 'PwdMeter',
24107                     tag: 'div',
24108                     cls: 'roo-password-meter-grey col-xs-12',
24109                     style: {
24110                         //width: 0,
24111                         //width: this.meterWidth + 'px'                                                
24112                         }
24113                     },
24114                     {                            
24115                          cls: 'roo-password-meter-text'                          
24116                     }
24117                 ]            
24118         });
24119
24120          
24121         if (this.hideTrigger) {
24122             this.trigger.setDisplayed(false);
24123         }
24124         this.setSize(this.width || '', this.height || '');
24125     },
24126     // private
24127     onDestroy: function ()
24128     {
24129         if (this.trigger) {
24130             this.trigger.removeAllListeners();
24131             this.trigger.remove();
24132         }
24133         if (this.wrap) {
24134             this.wrap.remove();
24135         }
24136         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24137     },
24138     // private
24139     checkStrength: function ()
24140     {
24141         var pwd = this.inputEl().getValue();
24142         if (pwd == this._lastPwd) {
24143             return;
24144         }
24145
24146         var strength;
24147         if (this.ClientSideStrongPassword(pwd)) {
24148             strength = 3;
24149         } else if (this.ClientSideMediumPassword(pwd)) {
24150             strength = 2;
24151         } else if (this.ClientSideWeakPassword(pwd)) {
24152             strength = 1;
24153         } else {
24154             strength = 0;
24155         }
24156         
24157         Roo.log('strength1: ' + strength);
24158         
24159         //var pm = this.trigger.child('div/div/div').dom;
24160         var pm = this.trigger.child('div/div');
24161         pm.removeClass(this.meterClass);
24162         pm.addClass(this.meterClass[strength]);
24163                 
24164         
24165         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24166                 
24167         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24168         
24169         this._lastPwd = pwd;
24170     },
24171     reset: function ()
24172     {
24173         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24174         
24175         this._lastPwd = '';
24176         
24177         var pm = this.trigger.child('div/div');
24178         pm.removeClass(this.meterClass);
24179         pm.addClass('roo-password-meter-grey');        
24180         
24181         
24182         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24183         
24184         pt.innerHTML = '';
24185         this.inputEl().dom.type='password';
24186     },
24187     // private
24188     validateValue: function (value)
24189     {
24190         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24191             return false;
24192         }
24193         if (value.length == 0) {
24194             if (this.allowBlank) {
24195                 this.clearInvalid();
24196                 return true;
24197             }
24198
24199             this.markInvalid(this.errors.PwdEmpty);
24200             this.errorMsg = this.errors.PwdEmpty;
24201             return false;
24202         }
24203         
24204         if(this.insecure){
24205             return true;
24206         }
24207         
24208         if (!value.match(/[\x21-\x7e]+/)) {
24209             this.markInvalid(this.errors.PwdBadChar);
24210             this.errorMsg = this.errors.PwdBadChar;
24211             return false;
24212         }
24213         if (value.length < 6) {
24214             this.markInvalid(this.errors.PwdShort);
24215             this.errorMsg = this.errors.PwdShort;
24216             return false;
24217         }
24218         if (value.length > 16) {
24219             this.markInvalid(this.errors.PwdLong);
24220             this.errorMsg = this.errors.PwdLong;
24221             return false;
24222         }
24223         var strength;
24224         if (this.ClientSideStrongPassword(value)) {
24225             strength = 3;
24226         } else if (this.ClientSideMediumPassword(value)) {
24227             strength = 2;
24228         } else if (this.ClientSideWeakPassword(value)) {
24229             strength = 1;
24230         } else {
24231             strength = 0;
24232         }
24233
24234         
24235         if (strength < 2) {
24236             //this.markInvalid(this.errors.TooWeak);
24237             this.errorMsg = this.errors.TooWeak;
24238             //return false;
24239         }
24240         
24241         
24242         console.log('strength2: ' + strength);
24243         
24244         //var pm = this.trigger.child('div/div/div').dom;
24245         
24246         var pm = this.trigger.child('div/div');
24247         pm.removeClass(this.meterClass);
24248         pm.addClass(this.meterClass[strength]);
24249                 
24250         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24251                 
24252         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24253         
24254         this.errorMsg = ''; 
24255         return true;
24256     },
24257     // private
24258     CharacterSetChecks: function (type)
24259     {
24260         this.type = type;
24261         this.fResult = false;
24262     },
24263     // private
24264     isctype: function (character, type)
24265     {
24266         switch (type) {  
24267             case this.kCapitalLetter:
24268                 if (character >= 'A' && character <= 'Z') {
24269                     return true;
24270                 }
24271                 break;
24272             
24273             case this.kSmallLetter:
24274                 if (character >= 'a' && character <= 'z') {
24275                     return true;
24276                 }
24277                 break;
24278             
24279             case this.kDigit:
24280                 if (character >= '0' && character <= '9') {
24281                     return true;
24282                 }
24283                 break;
24284             
24285             case this.kPunctuation:
24286                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24287                     return true;
24288                 }
24289                 break;
24290             
24291             default:
24292                 return false;
24293         }
24294
24295     },
24296     // private
24297     IsLongEnough: function (pwd, size)
24298     {
24299         return !(pwd == null || isNaN(size) || pwd.length < size);
24300     },
24301     // private
24302     SpansEnoughCharacterSets: function (word, nb)
24303     {
24304         if (!this.IsLongEnough(word, nb))
24305         {
24306             return false;
24307         }
24308
24309         var characterSetChecks = new Array(
24310             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24311             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24312         );
24313         
24314         for (var index = 0; index < word.length; ++index) {
24315             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24316                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24317                     characterSetChecks[nCharSet].fResult = true;
24318                     break;
24319                 }
24320             }
24321         }
24322
24323         var nCharSets = 0;
24324         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24325             if (characterSetChecks[nCharSet].fResult) {
24326                 ++nCharSets;
24327             }
24328         }
24329
24330         if (nCharSets < nb) {
24331             return false;
24332         }
24333         return true;
24334     },
24335     // private
24336     ClientSideStrongPassword: function (pwd)
24337     {
24338         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24339     },
24340     // private
24341     ClientSideMediumPassword: function (pwd)
24342     {
24343         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24344     },
24345     // private
24346     ClientSideWeakPassword: function (pwd)
24347     {
24348         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24349     }
24350           
24351 })//<script type="text/javascript">
24352
24353 /*
24354  * Based  Ext JS Library 1.1.1
24355  * Copyright(c) 2006-2007, Ext JS, LLC.
24356  * LGPL
24357  *
24358  */
24359  
24360 /**
24361  * @class Roo.HtmlEditorCore
24362  * @extends Roo.Component
24363  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24364  *
24365  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24366  */
24367
24368 Roo.HtmlEditorCore = function(config){
24369     
24370     
24371     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24372     
24373     
24374     this.addEvents({
24375         /**
24376          * @event initialize
24377          * Fires when the editor is fully initialized (including the iframe)
24378          * @param {Roo.HtmlEditorCore} this
24379          */
24380         initialize: true,
24381         /**
24382          * @event activate
24383          * Fires when the editor is first receives the focus. Any insertion must wait
24384          * until after this event.
24385          * @param {Roo.HtmlEditorCore} this
24386          */
24387         activate: true,
24388          /**
24389          * @event beforesync
24390          * Fires before the textarea is updated with content from the editor iframe. Return false
24391          * to cancel the sync.
24392          * @param {Roo.HtmlEditorCore} this
24393          * @param {String} html
24394          */
24395         beforesync: true,
24396          /**
24397          * @event beforepush
24398          * Fires before the iframe editor is updated with content from the textarea. Return false
24399          * to cancel the push.
24400          * @param {Roo.HtmlEditorCore} this
24401          * @param {String} html
24402          */
24403         beforepush: true,
24404          /**
24405          * @event sync
24406          * Fires when the textarea is updated with content from the editor iframe.
24407          * @param {Roo.HtmlEditorCore} this
24408          * @param {String} html
24409          */
24410         sync: true,
24411          /**
24412          * @event push
24413          * Fires when the iframe editor is updated with content from the textarea.
24414          * @param {Roo.HtmlEditorCore} this
24415          * @param {String} html
24416          */
24417         push: true,
24418         
24419         /**
24420          * @event editorevent
24421          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24422          * @param {Roo.HtmlEditorCore} this
24423          */
24424         editorevent: true
24425         
24426     });
24427     
24428     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24429     
24430     // defaults : white / black...
24431     this.applyBlacklists();
24432     
24433     
24434     
24435 };
24436
24437
24438 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24439
24440
24441      /**
24442      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24443      */
24444     
24445     owner : false,
24446     
24447      /**
24448      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24449      *                        Roo.resizable.
24450      */
24451     resizable : false,
24452      /**
24453      * @cfg {Number} height (in pixels)
24454      */   
24455     height: 300,
24456    /**
24457      * @cfg {Number} width (in pixels)
24458      */   
24459     width: 500,
24460     
24461     /**
24462      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24463      * 
24464      */
24465     stylesheets: false,
24466     
24467     // id of frame..
24468     frameId: false,
24469     
24470     // private properties
24471     validationEvent : false,
24472     deferHeight: true,
24473     initialized : false,
24474     activated : false,
24475     sourceEditMode : false,
24476     onFocus : Roo.emptyFn,
24477     iframePad:3,
24478     hideMode:'offsets',
24479     
24480     clearUp: true,
24481     
24482     // blacklist + whitelisted elements..
24483     black: false,
24484     white: false,
24485      
24486     bodyCls : '',
24487
24488     /**
24489      * Protected method that will not generally be called directly. It
24490      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24491      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24492      */
24493     getDocMarkup : function(){
24494         // body styles..
24495         var st = '';
24496         
24497         // inherit styels from page...?? 
24498         if (this.stylesheets === false) {
24499             
24500             Roo.get(document.head).select('style').each(function(node) {
24501                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24502             });
24503             
24504             Roo.get(document.head).select('link').each(function(node) { 
24505                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24506             });
24507             
24508         } else if (!this.stylesheets.length) {
24509                 // simple..
24510                 st = '<style type="text/css">' +
24511                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24512                    '</style>';
24513         } else {
24514             for (var i in this.stylesheets) { 
24515                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24516             }
24517             
24518         }
24519         
24520         st +=  '<style type="text/css">' +
24521             'IMG { cursor: pointer } ' +
24522         '</style>';
24523
24524         var cls = 'roo-htmleditor-body';
24525         
24526         if(this.bodyCls.length){
24527             cls += ' ' + this.bodyCls;
24528         }
24529         
24530         return '<html><head>' + st  +
24531             //<style type="text/css">' +
24532             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24533             //'</style>' +
24534             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24535     },
24536
24537     // private
24538     onRender : function(ct, position)
24539     {
24540         var _t = this;
24541         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24542         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24543         
24544         
24545         this.el.dom.style.border = '0 none';
24546         this.el.dom.setAttribute('tabIndex', -1);
24547         this.el.addClass('x-hidden hide');
24548         
24549         
24550         
24551         if(Roo.isIE){ // fix IE 1px bogus margin
24552             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24553         }
24554        
24555         
24556         this.frameId = Roo.id();
24557         
24558          
24559         
24560         var iframe = this.owner.wrap.createChild({
24561             tag: 'iframe',
24562             cls: 'form-control', // bootstrap..
24563             id: this.frameId,
24564             name: this.frameId,
24565             frameBorder : 'no',
24566             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24567         }, this.el
24568         );
24569         
24570         
24571         this.iframe = iframe.dom;
24572
24573          this.assignDocWin();
24574         
24575         this.doc.designMode = 'on';
24576        
24577         this.doc.open();
24578         this.doc.write(this.getDocMarkup());
24579         this.doc.close();
24580
24581         
24582         var task = { // must defer to wait for browser to be ready
24583             run : function(){
24584                 //console.log("run task?" + this.doc.readyState);
24585                 this.assignDocWin();
24586                 if(this.doc.body || this.doc.readyState == 'complete'){
24587                     try {
24588                         this.doc.designMode="on";
24589                     } catch (e) {
24590                         return;
24591                     }
24592                     Roo.TaskMgr.stop(task);
24593                     this.initEditor.defer(10, this);
24594                 }
24595             },
24596             interval : 10,
24597             duration: 10000,
24598             scope: this
24599         };
24600         Roo.TaskMgr.start(task);
24601
24602     },
24603
24604     // private
24605     onResize : function(w, h)
24606     {
24607          Roo.log('resize: ' +w + ',' + h );
24608         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24609         if(!this.iframe){
24610             return;
24611         }
24612         if(typeof w == 'number'){
24613             
24614             this.iframe.style.width = w + 'px';
24615         }
24616         if(typeof h == 'number'){
24617             
24618             this.iframe.style.height = h + 'px';
24619             if(this.doc){
24620                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24621             }
24622         }
24623         
24624     },
24625
24626     /**
24627      * Toggles the editor between standard and source edit mode.
24628      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24629      */
24630     toggleSourceEdit : function(sourceEditMode){
24631         
24632         this.sourceEditMode = sourceEditMode === true;
24633         
24634         if(this.sourceEditMode){
24635  
24636             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24637             
24638         }else{
24639             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24640             //this.iframe.className = '';
24641             this.deferFocus();
24642         }
24643         //this.setSize(this.owner.wrap.getSize());
24644         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24645     },
24646
24647     
24648   
24649
24650     /**
24651      * Protected method that will not generally be called directly. If you need/want
24652      * custom HTML cleanup, this is the method you should override.
24653      * @param {String} html The HTML to be cleaned
24654      * return {String} The cleaned HTML
24655      */
24656     cleanHtml : function(html){
24657         html = String(html);
24658         if(html.length > 5){
24659             if(Roo.isSafari){ // strip safari nonsense
24660                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24661             }
24662         }
24663         if(html == '&nbsp;'){
24664             html = '';
24665         }
24666         return html;
24667     },
24668
24669     /**
24670      * HTML Editor -> Textarea
24671      * Protected method that will not generally be called directly. Syncs the contents
24672      * of the editor iframe with the textarea.
24673      */
24674     syncValue : function(){
24675         if(this.initialized){
24676             var bd = (this.doc.body || this.doc.documentElement);
24677             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24678             var html = bd.innerHTML;
24679             if(Roo.isSafari){
24680                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24681                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24682                 if(m && m[1]){
24683                     html = '<div style="'+m[0]+'">' + html + '</div>';
24684                 }
24685             }
24686             html = this.cleanHtml(html);
24687             // fix up the special chars.. normaly like back quotes in word...
24688             // however we do not want to do this with chinese..
24689             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24690                 
24691                 var cc = match.charCodeAt();
24692
24693                 // Get the character value, handling surrogate pairs
24694                 if (match.length == 2) {
24695                     // It's a surrogate pair, calculate the Unicode code point
24696                     var high = match.charCodeAt(0) - 0xD800;
24697                     var low  = match.charCodeAt(1) - 0xDC00;
24698                     cc = (high * 0x400) + low + 0x10000;
24699                 }  else if (
24700                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24701                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24702                     (cc >= 0xf900 && cc < 0xfb00 )
24703                 ) {
24704                         return match;
24705                 }  
24706          
24707                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24708                 return "&#" + cc + ";";
24709                 
24710                 
24711             });
24712             
24713             
24714              
24715             if(this.owner.fireEvent('beforesync', this, html) !== false){
24716                 this.el.dom.value = html;
24717                 this.owner.fireEvent('sync', this, html);
24718             }
24719         }
24720     },
24721
24722     /**
24723      * Protected method that will not generally be called directly. Pushes the value of the textarea
24724      * into the iframe editor.
24725      */
24726     pushValue : function(){
24727         if(this.initialized){
24728             var v = this.el.dom.value.trim();
24729             
24730 //            if(v.length < 1){
24731 //                v = '&#160;';
24732 //            }
24733             
24734             if(this.owner.fireEvent('beforepush', this, v) !== false){
24735                 var d = (this.doc.body || this.doc.documentElement);
24736                 d.innerHTML = v;
24737                 this.cleanUpPaste();
24738                 this.el.dom.value = d.innerHTML;
24739                 this.owner.fireEvent('push', this, v);
24740             }
24741         }
24742     },
24743
24744     // private
24745     deferFocus : function(){
24746         this.focus.defer(10, this);
24747     },
24748
24749     // doc'ed in Field
24750     focus : function(){
24751         if(this.win && !this.sourceEditMode){
24752             this.win.focus();
24753         }else{
24754             this.el.focus();
24755         }
24756     },
24757     
24758     assignDocWin: function()
24759     {
24760         var iframe = this.iframe;
24761         
24762          if(Roo.isIE){
24763             this.doc = iframe.contentWindow.document;
24764             this.win = iframe.contentWindow;
24765         } else {
24766 //            if (!Roo.get(this.frameId)) {
24767 //                return;
24768 //            }
24769 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24770 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24771             
24772             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24773                 return;
24774             }
24775             
24776             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24777             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24778         }
24779     },
24780     
24781     // private
24782     initEditor : function(){
24783         //console.log("INIT EDITOR");
24784         this.assignDocWin();
24785         
24786         
24787         
24788         this.doc.designMode="on";
24789         this.doc.open();
24790         this.doc.write(this.getDocMarkup());
24791         this.doc.close();
24792         
24793         var dbody = (this.doc.body || this.doc.documentElement);
24794         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24795         // this copies styles from the containing element into thsi one..
24796         // not sure why we need all of this..
24797         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24798         
24799         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24800         //ss['background-attachment'] = 'fixed'; // w3c
24801         dbody.bgProperties = 'fixed'; // ie
24802         //Roo.DomHelper.applyStyles(dbody, ss);
24803         Roo.EventManager.on(this.doc, {
24804             //'mousedown': this.onEditorEvent,
24805             'mouseup': this.onEditorEvent,
24806             'dblclick': this.onEditorEvent,
24807             'click': this.onEditorEvent,
24808             'keyup': this.onEditorEvent,
24809             buffer:100,
24810             scope: this
24811         });
24812         if(Roo.isGecko){
24813             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24814         }
24815         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24816             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24817         }
24818         this.initialized = true;
24819
24820         this.owner.fireEvent('initialize', this);
24821         this.pushValue();
24822     },
24823
24824     // private
24825     onDestroy : function(){
24826         
24827         
24828         
24829         if(this.rendered){
24830             
24831             //for (var i =0; i < this.toolbars.length;i++) {
24832             //    // fixme - ask toolbars for heights?
24833             //    this.toolbars[i].onDestroy();
24834            // }
24835             
24836             //this.wrap.dom.innerHTML = '';
24837             //this.wrap.remove();
24838         }
24839     },
24840
24841     // private
24842     onFirstFocus : function(){
24843         
24844         this.assignDocWin();
24845         
24846         
24847         this.activated = true;
24848          
24849     
24850         if(Roo.isGecko){ // prevent silly gecko errors
24851             this.win.focus();
24852             var s = this.win.getSelection();
24853             if(!s.focusNode || s.focusNode.nodeType != 3){
24854                 var r = s.getRangeAt(0);
24855                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24856                 r.collapse(true);
24857                 this.deferFocus();
24858             }
24859             try{
24860                 this.execCmd('useCSS', true);
24861                 this.execCmd('styleWithCSS', false);
24862             }catch(e){}
24863         }
24864         this.owner.fireEvent('activate', this);
24865     },
24866
24867     // private
24868     adjustFont: function(btn){
24869         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24870         //if(Roo.isSafari){ // safari
24871         //    adjust *= 2;
24872        // }
24873         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24874         if(Roo.isSafari){ // safari
24875             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24876             v =  (v < 10) ? 10 : v;
24877             v =  (v > 48) ? 48 : v;
24878             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24879             
24880         }
24881         
24882         
24883         v = Math.max(1, v+adjust);
24884         
24885         this.execCmd('FontSize', v  );
24886     },
24887
24888     onEditorEvent : function(e)
24889     {
24890         this.owner.fireEvent('editorevent', this, e);
24891       //  this.updateToolbar();
24892         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24893     },
24894
24895     insertTag : function(tg)
24896     {
24897         // could be a bit smarter... -> wrap the current selected tRoo..
24898         if (tg.toLowerCase() == 'span' ||
24899             tg.toLowerCase() == 'code' ||
24900             tg.toLowerCase() == 'sup' ||
24901             tg.toLowerCase() == 'sub' 
24902             ) {
24903             
24904             range = this.createRange(this.getSelection());
24905             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24906             wrappingNode.appendChild(range.extractContents());
24907             range.insertNode(wrappingNode);
24908
24909             return;
24910             
24911             
24912             
24913         }
24914         this.execCmd("formatblock",   tg);
24915         
24916     },
24917     
24918     insertText : function(txt)
24919     {
24920         
24921         
24922         var range = this.createRange();
24923         range.deleteContents();
24924                //alert(Sender.getAttribute('label'));
24925                
24926         range.insertNode(this.doc.createTextNode(txt));
24927     } ,
24928     
24929      
24930
24931     /**
24932      * Executes a Midas editor command on the editor document and performs necessary focus and
24933      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24934      * @param {String} cmd The Midas command
24935      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24936      */
24937     relayCmd : function(cmd, value){
24938         this.win.focus();
24939         this.execCmd(cmd, value);
24940         this.owner.fireEvent('editorevent', this);
24941         //this.updateToolbar();
24942         this.owner.deferFocus();
24943     },
24944
24945     /**
24946      * Executes a Midas editor command directly on the editor document.
24947      * For visual commands, you should use {@link #relayCmd} instead.
24948      * <b>This should only be called after the editor is initialized.</b>
24949      * @param {String} cmd The Midas command
24950      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24951      */
24952     execCmd : function(cmd, value){
24953         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24954         this.syncValue();
24955     },
24956  
24957  
24958    
24959     /**
24960      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24961      * to insert tRoo.
24962      * @param {String} text | dom node.. 
24963      */
24964     insertAtCursor : function(text)
24965     {
24966         
24967         if(!this.activated){
24968             return;
24969         }
24970         /*
24971         if(Roo.isIE){
24972             this.win.focus();
24973             var r = this.doc.selection.createRange();
24974             if(r){
24975                 r.collapse(true);
24976                 r.pasteHTML(text);
24977                 this.syncValue();
24978                 this.deferFocus();
24979             
24980             }
24981             return;
24982         }
24983         */
24984         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24985             this.win.focus();
24986             
24987             
24988             // from jquery ui (MIT licenced)
24989             var range, node;
24990             var win = this.win;
24991             
24992             if (win.getSelection && win.getSelection().getRangeAt) {
24993                 range = win.getSelection().getRangeAt(0);
24994                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24995                 range.insertNode(node);
24996             } else if (win.document.selection && win.document.selection.createRange) {
24997                 // no firefox support
24998                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24999                 win.document.selection.createRange().pasteHTML(txt);
25000             } else {
25001                 // no firefox support
25002                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25003                 this.execCmd('InsertHTML', txt);
25004             } 
25005             
25006             this.syncValue();
25007             
25008             this.deferFocus();
25009         }
25010     },
25011  // private
25012     mozKeyPress : function(e){
25013         if(e.ctrlKey){
25014             var c = e.getCharCode(), cmd;
25015           
25016             if(c > 0){
25017                 c = String.fromCharCode(c).toLowerCase();
25018                 switch(c){
25019                     case 'b':
25020                         cmd = 'bold';
25021                         break;
25022                     case 'i':
25023                         cmd = 'italic';
25024                         break;
25025                     
25026                     case 'u':
25027                         cmd = 'underline';
25028                         break;
25029                     
25030                     case 'v':
25031                         this.cleanUpPaste.defer(100, this);
25032                         return;
25033                         
25034                 }
25035                 if(cmd){
25036                     this.win.focus();
25037                     this.execCmd(cmd);
25038                     this.deferFocus();
25039                     e.preventDefault();
25040                 }
25041                 
25042             }
25043         }
25044     },
25045
25046     // private
25047     fixKeys : function(){ // load time branching for fastest keydown performance
25048         if(Roo.isIE){
25049             return function(e){
25050                 var k = e.getKey(), r;
25051                 if(k == e.TAB){
25052                     e.stopEvent();
25053                     r = this.doc.selection.createRange();
25054                     if(r){
25055                         r.collapse(true);
25056                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25057                         this.deferFocus();
25058                     }
25059                     return;
25060                 }
25061                 
25062                 if(k == e.ENTER){
25063                     r = this.doc.selection.createRange();
25064                     if(r){
25065                         var target = r.parentElement();
25066                         if(!target || target.tagName.toLowerCase() != 'li'){
25067                             e.stopEvent();
25068                             r.pasteHTML('<br />');
25069                             r.collapse(false);
25070                             r.select();
25071                         }
25072                     }
25073                 }
25074                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25075                     this.cleanUpPaste.defer(100, this);
25076                     return;
25077                 }
25078                 
25079                 
25080             };
25081         }else if(Roo.isOpera){
25082             return function(e){
25083                 var k = e.getKey();
25084                 if(k == e.TAB){
25085                     e.stopEvent();
25086                     this.win.focus();
25087                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25088                     this.deferFocus();
25089                 }
25090                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25091                     this.cleanUpPaste.defer(100, this);
25092                     return;
25093                 }
25094                 
25095             };
25096         }else if(Roo.isSafari){
25097             return function(e){
25098                 var k = e.getKey();
25099                 
25100                 if(k == e.TAB){
25101                     e.stopEvent();
25102                     this.execCmd('InsertText','\t');
25103                     this.deferFocus();
25104                     return;
25105                 }
25106                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25107                     this.cleanUpPaste.defer(100, this);
25108                     return;
25109                 }
25110                 
25111              };
25112         }
25113     }(),
25114     
25115     getAllAncestors: function()
25116     {
25117         var p = this.getSelectedNode();
25118         var a = [];
25119         if (!p) {
25120             a.push(p); // push blank onto stack..
25121             p = this.getParentElement();
25122         }
25123         
25124         
25125         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25126             a.push(p);
25127             p = p.parentNode;
25128         }
25129         a.push(this.doc.body);
25130         return a;
25131     },
25132     lastSel : false,
25133     lastSelNode : false,
25134     
25135     
25136     getSelection : function() 
25137     {
25138         this.assignDocWin();
25139         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25140     },
25141     
25142     getSelectedNode: function() 
25143     {
25144         // this may only work on Gecko!!!
25145         
25146         // should we cache this!!!!
25147         
25148         
25149         
25150          
25151         var range = this.createRange(this.getSelection()).cloneRange();
25152         
25153         if (Roo.isIE) {
25154             var parent = range.parentElement();
25155             while (true) {
25156                 var testRange = range.duplicate();
25157                 testRange.moveToElementText(parent);
25158                 if (testRange.inRange(range)) {
25159                     break;
25160                 }
25161                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25162                     break;
25163                 }
25164                 parent = parent.parentElement;
25165             }
25166             return parent;
25167         }
25168         
25169         // is ancestor a text element.
25170         var ac =  range.commonAncestorContainer;
25171         if (ac.nodeType == 3) {
25172             ac = ac.parentNode;
25173         }
25174         
25175         var ar = ac.childNodes;
25176          
25177         var nodes = [];
25178         var other_nodes = [];
25179         var has_other_nodes = false;
25180         for (var i=0;i<ar.length;i++) {
25181             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25182                 continue;
25183             }
25184             // fullly contained node.
25185             
25186             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25187                 nodes.push(ar[i]);
25188                 continue;
25189             }
25190             
25191             // probably selected..
25192             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25193                 other_nodes.push(ar[i]);
25194                 continue;
25195             }
25196             // outer..
25197             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25198                 continue;
25199             }
25200             
25201             
25202             has_other_nodes = true;
25203         }
25204         if (!nodes.length && other_nodes.length) {
25205             nodes= other_nodes;
25206         }
25207         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25208             return false;
25209         }
25210         
25211         return nodes[0];
25212     },
25213     createRange: function(sel)
25214     {
25215         // this has strange effects when using with 
25216         // top toolbar - not sure if it's a great idea.
25217         //this.editor.contentWindow.focus();
25218         if (typeof sel != "undefined") {
25219             try {
25220                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25221             } catch(e) {
25222                 return this.doc.createRange();
25223             }
25224         } else {
25225             return this.doc.createRange();
25226         }
25227     },
25228     getParentElement: function()
25229     {
25230         
25231         this.assignDocWin();
25232         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25233         
25234         var range = this.createRange(sel);
25235          
25236         try {
25237             var p = range.commonAncestorContainer;
25238             while (p.nodeType == 3) { // text node
25239                 p = p.parentNode;
25240             }
25241             return p;
25242         } catch (e) {
25243             return null;
25244         }
25245     
25246     },
25247     /***
25248      *
25249      * Range intersection.. the hard stuff...
25250      *  '-1' = before
25251      *  '0' = hits..
25252      *  '1' = after.
25253      *         [ -- selected range --- ]
25254      *   [fail]                        [fail]
25255      *
25256      *    basically..
25257      *      if end is before start or  hits it. fail.
25258      *      if start is after end or hits it fail.
25259      *
25260      *   if either hits (but other is outside. - then it's not 
25261      *   
25262      *    
25263      **/
25264     
25265     
25266     // @see http://www.thismuchiknow.co.uk/?p=64.
25267     rangeIntersectsNode : function(range, node)
25268     {
25269         var nodeRange = node.ownerDocument.createRange();
25270         try {
25271             nodeRange.selectNode(node);
25272         } catch (e) {
25273             nodeRange.selectNodeContents(node);
25274         }
25275     
25276         var rangeStartRange = range.cloneRange();
25277         rangeStartRange.collapse(true);
25278     
25279         var rangeEndRange = range.cloneRange();
25280         rangeEndRange.collapse(false);
25281     
25282         var nodeStartRange = nodeRange.cloneRange();
25283         nodeStartRange.collapse(true);
25284     
25285         var nodeEndRange = nodeRange.cloneRange();
25286         nodeEndRange.collapse(false);
25287     
25288         return rangeStartRange.compareBoundaryPoints(
25289                  Range.START_TO_START, nodeEndRange) == -1 &&
25290                rangeEndRange.compareBoundaryPoints(
25291                  Range.START_TO_START, nodeStartRange) == 1;
25292         
25293          
25294     },
25295     rangeCompareNode : function(range, node)
25296     {
25297         var nodeRange = node.ownerDocument.createRange();
25298         try {
25299             nodeRange.selectNode(node);
25300         } catch (e) {
25301             nodeRange.selectNodeContents(node);
25302         }
25303         
25304         
25305         range.collapse(true);
25306     
25307         nodeRange.collapse(true);
25308      
25309         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25310         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25311          
25312         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25313         
25314         var nodeIsBefore   =  ss == 1;
25315         var nodeIsAfter    = ee == -1;
25316         
25317         if (nodeIsBefore && nodeIsAfter) {
25318             return 0; // outer
25319         }
25320         if (!nodeIsBefore && nodeIsAfter) {
25321             return 1; //right trailed.
25322         }
25323         
25324         if (nodeIsBefore && !nodeIsAfter) {
25325             return 2;  // left trailed.
25326         }
25327         // fully contined.
25328         return 3;
25329     },
25330
25331     // private? - in a new class?
25332     cleanUpPaste :  function()
25333     {
25334         // cleans up the whole document..
25335         Roo.log('cleanuppaste');
25336         
25337         this.cleanUpChildren(this.doc.body);
25338         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25339         if (clean != this.doc.body.innerHTML) {
25340             this.doc.body.innerHTML = clean;
25341         }
25342         
25343     },
25344     
25345     cleanWordChars : function(input) {// change the chars to hex code
25346         var he = Roo.HtmlEditorCore;
25347         
25348         var output = input;
25349         Roo.each(he.swapCodes, function(sw) { 
25350             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25351             
25352             output = output.replace(swapper, sw[1]);
25353         });
25354         
25355         return output;
25356     },
25357     
25358     
25359     cleanUpChildren : function (n)
25360     {
25361         if (!n.childNodes.length) {
25362             return;
25363         }
25364         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25365            this.cleanUpChild(n.childNodes[i]);
25366         }
25367     },
25368     
25369     
25370         
25371     
25372     cleanUpChild : function (node)
25373     {
25374         var ed = this;
25375         //console.log(node);
25376         if (node.nodeName == "#text") {
25377             // clean up silly Windows -- stuff?
25378             return; 
25379         }
25380         if (node.nodeName == "#comment") {
25381             node.parentNode.removeChild(node);
25382             // clean up silly Windows -- stuff?
25383             return; 
25384         }
25385         var lcname = node.tagName.toLowerCase();
25386         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25387         // whitelist of tags..
25388         
25389         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25390             // remove node.
25391             node.parentNode.removeChild(node);
25392             return;
25393             
25394         }
25395         
25396         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25397         
25398         // spans with no attributes - just remove them..
25399         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25400             remove_keep_children = true;
25401         }
25402         
25403         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25404         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25405         
25406         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25407         //    remove_keep_children = true;
25408         //}
25409         
25410         if (remove_keep_children) {
25411             this.cleanUpChildren(node);
25412             // inserts everything just before this node...
25413             while (node.childNodes.length) {
25414                 var cn = node.childNodes[0];
25415                 node.removeChild(cn);
25416                 node.parentNode.insertBefore(cn, node);
25417             }
25418             node.parentNode.removeChild(node);
25419             return;
25420         }
25421         
25422         if (!node.attributes || !node.attributes.length) {
25423             
25424           
25425             
25426             
25427             this.cleanUpChildren(node);
25428             return;
25429         }
25430         
25431         function cleanAttr(n,v)
25432         {
25433             
25434             if (v.match(/^\./) || v.match(/^\//)) {
25435                 return;
25436             }
25437             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25438                 return;
25439             }
25440             if (v.match(/^#/)) {
25441                 return;
25442             }
25443             if (v.match(/^\{/)) { // allow template editing.
25444                 return;
25445             }
25446 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25447             node.removeAttribute(n);
25448             
25449         }
25450         
25451         var cwhite = this.cwhite;
25452         var cblack = this.cblack;
25453             
25454         function cleanStyle(n,v)
25455         {
25456             if (v.match(/expression/)) { //XSS?? should we even bother..
25457                 node.removeAttribute(n);
25458                 return;
25459             }
25460             
25461             var parts = v.split(/;/);
25462             var clean = [];
25463             
25464             Roo.each(parts, function(p) {
25465                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25466                 if (!p.length) {
25467                     return true;
25468                 }
25469                 var l = p.split(':').shift().replace(/\s+/g,'');
25470                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25471                 
25472                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25473 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25474                     //node.removeAttribute(n);
25475                     return true;
25476                 }
25477                 //Roo.log()
25478                 // only allow 'c whitelisted system attributes'
25479                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25480 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25481                     //node.removeAttribute(n);
25482                     return true;
25483                 }
25484                 
25485                 
25486                  
25487                 
25488                 clean.push(p);
25489                 return true;
25490             });
25491             if (clean.length) { 
25492                 node.setAttribute(n, clean.join(';'));
25493             } else {
25494                 node.removeAttribute(n);
25495             }
25496             
25497         }
25498         
25499         
25500         for (var i = node.attributes.length-1; i > -1 ; i--) {
25501             var a = node.attributes[i];
25502             //console.log(a);
25503             
25504             if (a.name.toLowerCase().substr(0,2)=='on')  {
25505                 node.removeAttribute(a.name);
25506                 continue;
25507             }
25508             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25509                 node.removeAttribute(a.name);
25510                 continue;
25511             }
25512             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25513                 cleanAttr(a.name,a.value); // fixme..
25514                 continue;
25515             }
25516             if (a.name == 'style') {
25517                 cleanStyle(a.name,a.value);
25518                 continue;
25519             }
25520             /// clean up MS crap..
25521             // tecnically this should be a list of valid class'es..
25522             
25523             
25524             if (a.name == 'class') {
25525                 if (a.value.match(/^Mso/)) {
25526                     node.removeAttribute('class');
25527                 }
25528                 
25529                 if (a.value.match(/^body$/)) {
25530                     node.removeAttribute('class');
25531                 }
25532                 continue;
25533             }
25534             
25535             // style cleanup!?
25536             // class cleanup?
25537             
25538         }
25539         
25540         
25541         this.cleanUpChildren(node);
25542         
25543         
25544     },
25545     
25546     /**
25547      * Clean up MS wordisms...
25548      */
25549     cleanWord : function(node)
25550     {
25551         if (!node) {
25552             this.cleanWord(this.doc.body);
25553             return;
25554         }
25555         
25556         if(
25557                 node.nodeName == 'SPAN' &&
25558                 !node.hasAttributes() &&
25559                 node.childNodes.length == 1 &&
25560                 node.firstChild.nodeName == "#text"  
25561         ) {
25562             var textNode = node.firstChild;
25563             node.removeChild(textNode);
25564             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25565                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25566             }
25567             node.parentNode.insertBefore(textNode, node);
25568             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25569                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25570             }
25571             node.parentNode.removeChild(node);
25572         }
25573         
25574         if (node.nodeName == "#text") {
25575             // clean up silly Windows -- stuff?
25576             return; 
25577         }
25578         if (node.nodeName == "#comment") {
25579             node.parentNode.removeChild(node);
25580             // clean up silly Windows -- stuff?
25581             return; 
25582         }
25583         
25584         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25585             node.parentNode.removeChild(node);
25586             return;
25587         }
25588         //Roo.log(node.tagName);
25589         // remove - but keep children..
25590         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25591             //Roo.log('-- removed');
25592             while (node.childNodes.length) {
25593                 var cn = node.childNodes[0];
25594                 node.removeChild(cn);
25595                 node.parentNode.insertBefore(cn, node);
25596                 // move node to parent - and clean it..
25597                 this.cleanWord(cn);
25598             }
25599             node.parentNode.removeChild(node);
25600             /// no need to iterate chidlren = it's got none..
25601             //this.iterateChildren(node, this.cleanWord);
25602             return;
25603         }
25604         // clean styles
25605         if (node.className.length) {
25606             
25607             var cn = node.className.split(/\W+/);
25608             var cna = [];
25609             Roo.each(cn, function(cls) {
25610                 if (cls.match(/Mso[a-zA-Z]+/)) {
25611                     return;
25612                 }
25613                 cna.push(cls);
25614             });
25615             node.className = cna.length ? cna.join(' ') : '';
25616             if (!cna.length) {
25617                 node.removeAttribute("class");
25618             }
25619         }
25620         
25621         if (node.hasAttribute("lang")) {
25622             node.removeAttribute("lang");
25623         }
25624         
25625         if (node.hasAttribute("style")) {
25626             
25627             var styles = node.getAttribute("style").split(";");
25628             var nstyle = [];
25629             Roo.each(styles, function(s) {
25630                 if (!s.match(/:/)) {
25631                     return;
25632                 }
25633                 var kv = s.split(":");
25634                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25635                     return;
25636                 }
25637                 // what ever is left... we allow.
25638                 nstyle.push(s);
25639             });
25640             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25641             if (!nstyle.length) {
25642                 node.removeAttribute('style');
25643             }
25644         }
25645         this.iterateChildren(node, this.cleanWord);
25646         
25647         
25648         
25649     },
25650     /**
25651      * iterateChildren of a Node, calling fn each time, using this as the scole..
25652      * @param {DomNode} node node to iterate children of.
25653      * @param {Function} fn method of this class to call on each item.
25654      */
25655     iterateChildren : function(node, fn)
25656     {
25657         if (!node.childNodes.length) {
25658                 return;
25659         }
25660         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25661            fn.call(this, node.childNodes[i])
25662         }
25663     },
25664     
25665     
25666     /**
25667      * cleanTableWidths.
25668      *
25669      * Quite often pasting from word etc.. results in tables with column and widths.
25670      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25671      *
25672      */
25673     cleanTableWidths : function(node)
25674     {
25675          
25676          
25677         if (!node) {
25678             this.cleanTableWidths(this.doc.body);
25679             return;
25680         }
25681         
25682         // ignore list...
25683         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25684             return; 
25685         }
25686         Roo.log(node.tagName);
25687         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25688             this.iterateChildren(node, this.cleanTableWidths);
25689             return;
25690         }
25691         if (node.hasAttribute('width')) {
25692             node.removeAttribute('width');
25693         }
25694         
25695          
25696         if (node.hasAttribute("style")) {
25697             // pretty basic...
25698             
25699             var styles = node.getAttribute("style").split(";");
25700             var nstyle = [];
25701             Roo.each(styles, function(s) {
25702                 if (!s.match(/:/)) {
25703                     return;
25704                 }
25705                 var kv = s.split(":");
25706                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25707                     return;
25708                 }
25709                 // what ever is left... we allow.
25710                 nstyle.push(s);
25711             });
25712             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25713             if (!nstyle.length) {
25714                 node.removeAttribute('style');
25715             }
25716         }
25717         
25718         this.iterateChildren(node, this.cleanTableWidths);
25719         
25720         
25721     },
25722     
25723     
25724     
25725     
25726     domToHTML : function(currentElement, depth, nopadtext) {
25727         
25728         depth = depth || 0;
25729         nopadtext = nopadtext || false;
25730     
25731         if (!currentElement) {
25732             return this.domToHTML(this.doc.body);
25733         }
25734         
25735         //Roo.log(currentElement);
25736         var j;
25737         var allText = false;
25738         var nodeName = currentElement.nodeName;
25739         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25740         
25741         if  (nodeName == '#text') {
25742             
25743             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25744         }
25745         
25746         
25747         var ret = '';
25748         if (nodeName != 'BODY') {
25749              
25750             var i = 0;
25751             // Prints the node tagName, such as <A>, <IMG>, etc
25752             if (tagName) {
25753                 var attr = [];
25754                 for(i = 0; i < currentElement.attributes.length;i++) {
25755                     // quoting?
25756                     var aname = currentElement.attributes.item(i).name;
25757                     if (!currentElement.attributes.item(i).value.length) {
25758                         continue;
25759                     }
25760                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25761                 }
25762                 
25763                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25764             } 
25765             else {
25766                 
25767                 // eack
25768             }
25769         } else {
25770             tagName = false;
25771         }
25772         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25773             return ret;
25774         }
25775         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25776             nopadtext = true;
25777         }
25778         
25779         
25780         // Traverse the tree
25781         i = 0;
25782         var currentElementChild = currentElement.childNodes.item(i);
25783         var allText = true;
25784         var innerHTML  = '';
25785         lastnode = '';
25786         while (currentElementChild) {
25787             // Formatting code (indent the tree so it looks nice on the screen)
25788             var nopad = nopadtext;
25789             if (lastnode == 'SPAN') {
25790                 nopad  = true;
25791             }
25792             // text
25793             if  (currentElementChild.nodeName == '#text') {
25794                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25795                 toadd = nopadtext ? toadd : toadd.trim();
25796                 if (!nopad && toadd.length > 80) {
25797                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25798                 }
25799                 innerHTML  += toadd;
25800                 
25801                 i++;
25802                 currentElementChild = currentElement.childNodes.item(i);
25803                 lastNode = '';
25804                 continue;
25805             }
25806             allText = false;
25807             
25808             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25809                 
25810             // Recursively traverse the tree structure of the child node
25811             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25812             lastnode = currentElementChild.nodeName;
25813             i++;
25814             currentElementChild=currentElement.childNodes.item(i);
25815         }
25816         
25817         ret += innerHTML;
25818         
25819         if (!allText) {
25820                 // The remaining code is mostly for formatting the tree
25821             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25822         }
25823         
25824         
25825         if (tagName) {
25826             ret+= "</"+tagName+">";
25827         }
25828         return ret;
25829         
25830     },
25831         
25832     applyBlacklists : function()
25833     {
25834         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25835         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25836         
25837         this.white = [];
25838         this.black = [];
25839         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25840             if (b.indexOf(tag) > -1) {
25841                 return;
25842             }
25843             this.white.push(tag);
25844             
25845         }, this);
25846         
25847         Roo.each(w, function(tag) {
25848             if (b.indexOf(tag) > -1) {
25849                 return;
25850             }
25851             if (this.white.indexOf(tag) > -1) {
25852                 return;
25853             }
25854             this.white.push(tag);
25855             
25856         }, this);
25857         
25858         
25859         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25860             if (w.indexOf(tag) > -1) {
25861                 return;
25862             }
25863             this.black.push(tag);
25864             
25865         }, this);
25866         
25867         Roo.each(b, function(tag) {
25868             if (w.indexOf(tag) > -1) {
25869                 return;
25870             }
25871             if (this.black.indexOf(tag) > -1) {
25872                 return;
25873             }
25874             this.black.push(tag);
25875             
25876         }, this);
25877         
25878         
25879         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25880         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25881         
25882         this.cwhite = [];
25883         this.cblack = [];
25884         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25885             if (b.indexOf(tag) > -1) {
25886                 return;
25887             }
25888             this.cwhite.push(tag);
25889             
25890         }, this);
25891         
25892         Roo.each(w, function(tag) {
25893             if (b.indexOf(tag) > -1) {
25894                 return;
25895             }
25896             if (this.cwhite.indexOf(tag) > -1) {
25897                 return;
25898             }
25899             this.cwhite.push(tag);
25900             
25901         }, this);
25902         
25903         
25904         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25905             if (w.indexOf(tag) > -1) {
25906                 return;
25907             }
25908             this.cblack.push(tag);
25909             
25910         }, this);
25911         
25912         Roo.each(b, function(tag) {
25913             if (w.indexOf(tag) > -1) {
25914                 return;
25915             }
25916             if (this.cblack.indexOf(tag) > -1) {
25917                 return;
25918             }
25919             this.cblack.push(tag);
25920             
25921         }, this);
25922     },
25923     
25924     setStylesheets : function(stylesheets)
25925     {
25926         if(typeof(stylesheets) == 'string'){
25927             Roo.get(this.iframe.contentDocument.head).createChild({
25928                 tag : 'link',
25929                 rel : 'stylesheet',
25930                 type : 'text/css',
25931                 href : stylesheets
25932             });
25933             
25934             return;
25935         }
25936         var _this = this;
25937      
25938         Roo.each(stylesheets, function(s) {
25939             if(!s.length){
25940                 return;
25941             }
25942             
25943             Roo.get(_this.iframe.contentDocument.head).createChild({
25944                 tag : 'link',
25945                 rel : 'stylesheet',
25946                 type : 'text/css',
25947                 href : s
25948             });
25949         });
25950
25951         
25952     },
25953     
25954     removeStylesheets : function()
25955     {
25956         var _this = this;
25957         
25958         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25959             s.remove();
25960         });
25961     },
25962     
25963     setStyle : function(style)
25964     {
25965         Roo.get(this.iframe.contentDocument.head).createChild({
25966             tag : 'style',
25967             type : 'text/css',
25968             html : style
25969         });
25970
25971         return;
25972     }
25973     
25974     // hide stuff that is not compatible
25975     /**
25976      * @event blur
25977      * @hide
25978      */
25979     /**
25980      * @event change
25981      * @hide
25982      */
25983     /**
25984      * @event focus
25985      * @hide
25986      */
25987     /**
25988      * @event specialkey
25989      * @hide
25990      */
25991     /**
25992      * @cfg {String} fieldClass @hide
25993      */
25994     /**
25995      * @cfg {String} focusClass @hide
25996      */
25997     /**
25998      * @cfg {String} autoCreate @hide
25999      */
26000     /**
26001      * @cfg {String} inputType @hide
26002      */
26003     /**
26004      * @cfg {String} invalidClass @hide
26005      */
26006     /**
26007      * @cfg {String} invalidText @hide
26008      */
26009     /**
26010      * @cfg {String} msgFx @hide
26011      */
26012     /**
26013      * @cfg {String} validateOnBlur @hide
26014      */
26015 });
26016
26017 Roo.HtmlEditorCore.white = [
26018         'area', 'br', 'img', 'input', 'hr', 'wbr',
26019         
26020        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26021        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26022        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26023        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26024        'table',   'ul',         'xmp', 
26025        
26026        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26027       'thead',   'tr', 
26028      
26029       'dir', 'menu', 'ol', 'ul', 'dl',
26030        
26031       'embed',  'object'
26032 ];
26033
26034
26035 Roo.HtmlEditorCore.black = [
26036     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26037         'applet', // 
26038         'base',   'basefont', 'bgsound', 'blink',  'body', 
26039         'frame',  'frameset', 'head',    'html',   'ilayer', 
26040         'iframe', 'layer',  'link',     'meta',    'object',   
26041         'script', 'style' ,'title',  'xml' // clean later..
26042 ];
26043 Roo.HtmlEditorCore.clean = [
26044     'script', 'style', 'title', 'xml'
26045 ];
26046 Roo.HtmlEditorCore.remove = [
26047     'font'
26048 ];
26049 // attributes..
26050
26051 Roo.HtmlEditorCore.ablack = [
26052     'on'
26053 ];
26054     
26055 Roo.HtmlEditorCore.aclean = [ 
26056     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26057 ];
26058
26059 // protocols..
26060 Roo.HtmlEditorCore.pwhite= [
26061         'http',  'https',  'mailto'
26062 ];
26063
26064 // white listed style attributes.
26065 Roo.HtmlEditorCore.cwhite= [
26066       //  'text-align', /// default is to allow most things..
26067       
26068          
26069 //        'font-size'//??
26070 ];
26071
26072 // black listed style attributes.
26073 Roo.HtmlEditorCore.cblack= [
26074       //  'font-size' -- this can be set by the project 
26075 ];
26076
26077
26078 Roo.HtmlEditorCore.swapCodes   =[ 
26079     [    8211, "&#8211;" ], 
26080     [    8212, "&#8212;" ], 
26081     [    8216,  "'" ],  
26082     [    8217, "'" ],  
26083     [    8220, '"' ],  
26084     [    8221, '"' ],  
26085     [    8226, "*" ],  
26086     [    8230, "..." ]
26087 ]; 
26088
26089     /*
26090  * - LGPL
26091  *
26092  * HtmlEditor
26093  * 
26094  */
26095
26096 /**
26097  * @class Roo.bootstrap.HtmlEditor
26098  * @extends Roo.bootstrap.TextArea
26099  * Bootstrap HtmlEditor class
26100
26101  * @constructor
26102  * Create a new HtmlEditor
26103  * @param {Object} config The config object
26104  */
26105
26106 Roo.bootstrap.HtmlEditor = function(config){
26107     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26108     if (!this.toolbars) {
26109         this.toolbars = [];
26110     }
26111     
26112     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26113     this.addEvents({
26114             /**
26115              * @event initialize
26116              * Fires when the editor is fully initialized (including the iframe)
26117              * @param {HtmlEditor} this
26118              */
26119             initialize: true,
26120             /**
26121              * @event activate
26122              * Fires when the editor is first receives the focus. Any insertion must wait
26123              * until after this event.
26124              * @param {HtmlEditor} this
26125              */
26126             activate: true,
26127              /**
26128              * @event beforesync
26129              * Fires before the textarea is updated with content from the editor iframe. Return false
26130              * to cancel the sync.
26131              * @param {HtmlEditor} this
26132              * @param {String} html
26133              */
26134             beforesync: true,
26135              /**
26136              * @event beforepush
26137              * Fires before the iframe editor is updated with content from the textarea. Return false
26138              * to cancel the push.
26139              * @param {HtmlEditor} this
26140              * @param {String} html
26141              */
26142             beforepush: true,
26143              /**
26144              * @event sync
26145              * Fires when the textarea is updated with content from the editor iframe.
26146              * @param {HtmlEditor} this
26147              * @param {String} html
26148              */
26149             sync: true,
26150              /**
26151              * @event push
26152              * Fires when the iframe editor is updated with content from the textarea.
26153              * @param {HtmlEditor} this
26154              * @param {String} html
26155              */
26156             push: true,
26157              /**
26158              * @event editmodechange
26159              * Fires when the editor switches edit modes
26160              * @param {HtmlEditor} this
26161              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26162              */
26163             editmodechange: true,
26164             /**
26165              * @event editorevent
26166              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26167              * @param {HtmlEditor} this
26168              */
26169             editorevent: true,
26170             /**
26171              * @event firstfocus
26172              * Fires when on first focus - needed by toolbars..
26173              * @param {HtmlEditor} this
26174              */
26175             firstfocus: true,
26176             /**
26177              * @event autosave
26178              * Auto save the htmlEditor value as a file into Events
26179              * @param {HtmlEditor} this
26180              */
26181             autosave: true,
26182             /**
26183              * @event savedpreview
26184              * preview the saved version of htmlEditor
26185              * @param {HtmlEditor} this
26186              */
26187             savedpreview: true
26188         });
26189 };
26190
26191
26192 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26193     
26194     
26195       /**
26196      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26197      */
26198     toolbars : false,
26199     
26200      /**
26201     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26202     */
26203     btns : [],
26204    
26205      /**
26206      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26207      *                        Roo.resizable.
26208      */
26209     resizable : false,
26210      /**
26211      * @cfg {Number} height (in pixels)
26212      */   
26213     height: 300,
26214    /**
26215      * @cfg {Number} width (in pixels)
26216      */   
26217     width: false,
26218     
26219     /**
26220      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26221      * 
26222      */
26223     stylesheets: false,
26224     
26225     // id of frame..
26226     frameId: false,
26227     
26228     // private properties
26229     validationEvent : false,
26230     deferHeight: true,
26231     initialized : false,
26232     activated : false,
26233     
26234     onFocus : Roo.emptyFn,
26235     iframePad:3,
26236     hideMode:'offsets',
26237     
26238     tbContainer : false,
26239     
26240     bodyCls : '',
26241     
26242     toolbarContainer :function() {
26243         return this.wrap.select('.x-html-editor-tb',true).first();
26244     },
26245
26246     /**
26247      * Protected method that will not generally be called directly. It
26248      * is called when the editor creates its toolbar. Override this method if you need to
26249      * add custom toolbar buttons.
26250      * @param {HtmlEditor} editor
26251      */
26252     createToolbar : function(){
26253         Roo.log('renewing');
26254         Roo.log("create toolbars");
26255         
26256         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26257         this.toolbars[0].render(this.toolbarContainer());
26258         
26259         return;
26260         
26261 //        if (!editor.toolbars || !editor.toolbars.length) {
26262 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26263 //        }
26264 //        
26265 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26266 //            editor.toolbars[i] = Roo.factory(
26267 //                    typeof(editor.toolbars[i]) == 'string' ?
26268 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26269 //                Roo.bootstrap.HtmlEditor);
26270 //            editor.toolbars[i].init(editor);
26271 //        }
26272     },
26273
26274      
26275     // private
26276     onRender : function(ct, position)
26277     {
26278        // Roo.log("Call onRender: " + this.xtype);
26279         var _t = this;
26280         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26281       
26282         this.wrap = this.inputEl().wrap({
26283             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26284         });
26285         
26286         this.editorcore.onRender(ct, position);
26287          
26288         if (this.resizable) {
26289             this.resizeEl = new Roo.Resizable(this.wrap, {
26290                 pinned : true,
26291                 wrap: true,
26292                 dynamic : true,
26293                 minHeight : this.height,
26294                 height: this.height,
26295                 handles : this.resizable,
26296                 width: this.width,
26297                 listeners : {
26298                     resize : function(r, w, h) {
26299                         _t.onResize(w,h); // -something
26300                     }
26301                 }
26302             });
26303             
26304         }
26305         this.createToolbar(this);
26306        
26307         
26308         if(!this.width && this.resizable){
26309             this.setSize(this.wrap.getSize());
26310         }
26311         if (this.resizeEl) {
26312             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26313             // should trigger onReize..
26314         }
26315         
26316     },
26317
26318     // private
26319     onResize : function(w, h)
26320     {
26321         Roo.log('resize: ' +w + ',' + h );
26322         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26323         var ew = false;
26324         var eh = false;
26325         
26326         if(this.inputEl() ){
26327             if(typeof w == 'number'){
26328                 var aw = w - this.wrap.getFrameWidth('lr');
26329                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26330                 ew = aw;
26331             }
26332             if(typeof h == 'number'){
26333                  var tbh = -11;  // fixme it needs to tool bar size!
26334                 for (var i =0; i < this.toolbars.length;i++) {
26335                     // fixme - ask toolbars for heights?
26336                     tbh += this.toolbars[i].el.getHeight();
26337                     //if (this.toolbars[i].footer) {
26338                     //    tbh += this.toolbars[i].footer.el.getHeight();
26339                     //}
26340                 }
26341               
26342                 
26343                 
26344                 
26345                 
26346                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26347                 ah -= 5; // knock a few pixes off for look..
26348                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26349                 var eh = ah;
26350             }
26351         }
26352         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26353         this.editorcore.onResize(ew,eh);
26354         
26355     },
26356
26357     /**
26358      * Toggles the editor between standard and source edit mode.
26359      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26360      */
26361     toggleSourceEdit : function(sourceEditMode)
26362     {
26363         this.editorcore.toggleSourceEdit(sourceEditMode);
26364         
26365         if(this.editorcore.sourceEditMode){
26366             Roo.log('editor - showing textarea');
26367             
26368 //            Roo.log('in');
26369 //            Roo.log(this.syncValue());
26370             this.syncValue();
26371             this.inputEl().removeClass(['hide', 'x-hidden']);
26372             this.inputEl().dom.removeAttribute('tabIndex');
26373             this.inputEl().focus();
26374         }else{
26375             Roo.log('editor - hiding textarea');
26376 //            Roo.log('out')
26377 //            Roo.log(this.pushValue()); 
26378             this.pushValue();
26379             
26380             this.inputEl().addClass(['hide', 'x-hidden']);
26381             this.inputEl().dom.setAttribute('tabIndex', -1);
26382             //this.deferFocus();
26383         }
26384          
26385         if(this.resizable){
26386             this.setSize(this.wrap.getSize());
26387         }
26388         
26389         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26390     },
26391  
26392     // private (for BoxComponent)
26393     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26394
26395     // private (for BoxComponent)
26396     getResizeEl : function(){
26397         return this.wrap;
26398     },
26399
26400     // private (for BoxComponent)
26401     getPositionEl : function(){
26402         return this.wrap;
26403     },
26404
26405     // private
26406     initEvents : function(){
26407         this.originalValue = this.getValue();
26408     },
26409
26410 //    /**
26411 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26412 //     * @method
26413 //     */
26414 //    markInvalid : Roo.emptyFn,
26415 //    /**
26416 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26417 //     * @method
26418 //     */
26419 //    clearInvalid : Roo.emptyFn,
26420
26421     setValue : function(v){
26422         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26423         this.editorcore.pushValue();
26424     },
26425
26426      
26427     // private
26428     deferFocus : function(){
26429         this.focus.defer(10, this);
26430     },
26431
26432     // doc'ed in Field
26433     focus : function(){
26434         this.editorcore.focus();
26435         
26436     },
26437       
26438
26439     // private
26440     onDestroy : function(){
26441         
26442         
26443         
26444         if(this.rendered){
26445             
26446             for (var i =0; i < this.toolbars.length;i++) {
26447                 // fixme - ask toolbars for heights?
26448                 this.toolbars[i].onDestroy();
26449             }
26450             
26451             this.wrap.dom.innerHTML = '';
26452             this.wrap.remove();
26453         }
26454     },
26455
26456     // private
26457     onFirstFocus : function(){
26458         //Roo.log("onFirstFocus");
26459         this.editorcore.onFirstFocus();
26460          for (var i =0; i < this.toolbars.length;i++) {
26461             this.toolbars[i].onFirstFocus();
26462         }
26463         
26464     },
26465     
26466     // private
26467     syncValue : function()
26468     {   
26469         this.editorcore.syncValue();
26470     },
26471     
26472     pushValue : function()
26473     {   
26474         this.editorcore.pushValue();
26475     }
26476      
26477     
26478     // hide stuff that is not compatible
26479     /**
26480      * @event blur
26481      * @hide
26482      */
26483     /**
26484      * @event change
26485      * @hide
26486      */
26487     /**
26488      * @event focus
26489      * @hide
26490      */
26491     /**
26492      * @event specialkey
26493      * @hide
26494      */
26495     /**
26496      * @cfg {String} fieldClass @hide
26497      */
26498     /**
26499      * @cfg {String} focusClass @hide
26500      */
26501     /**
26502      * @cfg {String} autoCreate @hide
26503      */
26504     /**
26505      * @cfg {String} inputType @hide
26506      */
26507      
26508     /**
26509      * @cfg {String} invalidText @hide
26510      */
26511     /**
26512      * @cfg {String} msgFx @hide
26513      */
26514     /**
26515      * @cfg {String} validateOnBlur @hide
26516      */
26517 });
26518  
26519     
26520    
26521    
26522    
26523       
26524 Roo.namespace('Roo.bootstrap.htmleditor');
26525 /**
26526  * @class Roo.bootstrap.HtmlEditorToolbar1
26527  * Basic Toolbar
26528  * 
26529  * @example
26530  * Usage:
26531  *
26532  new Roo.bootstrap.HtmlEditor({
26533     ....
26534     toolbars : [
26535         new Roo.bootstrap.HtmlEditorToolbar1({
26536             disable : { fonts: 1 , format: 1, ..., ... , ...],
26537             btns : [ .... ]
26538         })
26539     }
26540      
26541  * 
26542  * @cfg {Object} disable List of elements to disable..
26543  * @cfg {Array} btns List of additional buttons.
26544  * 
26545  * 
26546  * NEEDS Extra CSS? 
26547  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26548  */
26549  
26550 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26551 {
26552     
26553     Roo.apply(this, config);
26554     
26555     // default disabled, based on 'good practice'..
26556     this.disable = this.disable || {};
26557     Roo.applyIf(this.disable, {
26558         fontSize : true,
26559         colors : true,
26560         specialElements : true
26561     });
26562     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26563     
26564     this.editor = config.editor;
26565     this.editorcore = config.editor.editorcore;
26566     
26567     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26568     
26569     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26570     // dont call parent... till later.
26571 }
26572 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26573      
26574     bar : true,
26575     
26576     editor : false,
26577     editorcore : false,
26578     
26579     
26580     formats : [
26581         "p" ,  
26582         "h1","h2","h3","h4","h5","h6", 
26583         "pre", "code", 
26584         "abbr", "acronym", "address", "cite", "samp", "var",
26585         'div','span'
26586     ],
26587     
26588     onRender : function(ct, position)
26589     {
26590        // Roo.log("Call onRender: " + this.xtype);
26591         
26592        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26593        Roo.log(this.el);
26594        this.el.dom.style.marginBottom = '0';
26595        var _this = this;
26596        var editorcore = this.editorcore;
26597        var editor= this.editor;
26598        
26599        var children = [];
26600        var btn = function(id,cmd , toggle, handler, html){
26601        
26602             var  event = toggle ? 'toggle' : 'click';
26603        
26604             var a = {
26605                 size : 'sm',
26606                 xtype: 'Button',
26607                 xns: Roo.bootstrap,
26608                 //glyphicon : id,
26609                 fa: id,
26610                 cmd : id || cmd,
26611                 enableToggle:toggle !== false,
26612                 html : html || '',
26613                 pressed : toggle ? false : null,
26614                 listeners : {}
26615             };
26616             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26617                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26618             };
26619             children.push(a);
26620             return a;
26621        }
26622        
26623     //    var cb_box = function...
26624         
26625         var style = {
26626                 xtype: 'Button',
26627                 size : 'sm',
26628                 xns: Roo.bootstrap,
26629                 fa : 'font',
26630                 //html : 'submit'
26631                 menu : {
26632                     xtype: 'Menu',
26633                     xns: Roo.bootstrap,
26634                     items:  []
26635                 }
26636         };
26637         Roo.each(this.formats, function(f) {
26638             style.menu.items.push({
26639                 xtype :'MenuItem',
26640                 xns: Roo.bootstrap,
26641                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26642                 tagname : f,
26643                 listeners : {
26644                     click : function()
26645                     {
26646                         editorcore.insertTag(this.tagname);
26647                         editor.focus();
26648                     }
26649                 }
26650                 
26651             });
26652         });
26653         children.push(style);   
26654         
26655         btn('bold',false,true);
26656         btn('italic',false,true);
26657         btn('align-left', 'justifyleft',true);
26658         btn('align-center', 'justifycenter',true);
26659         btn('align-right' , 'justifyright',true);
26660         btn('link', false, false, function(btn) {
26661             //Roo.log("create link?");
26662             var url = prompt(this.createLinkText, this.defaultLinkValue);
26663             if(url && url != 'http:/'+'/'){
26664                 this.editorcore.relayCmd('createlink', url);
26665             }
26666         }),
26667         btn('list','insertunorderedlist',true);
26668         btn('pencil', false,true, function(btn){
26669                 Roo.log(this);
26670                 this.toggleSourceEdit(btn.pressed);
26671         });
26672         
26673         if (this.editor.btns.length > 0) {
26674             for (var i = 0; i<this.editor.btns.length; i++) {
26675                 children.push(this.editor.btns[i]);
26676             }
26677         }
26678         
26679         /*
26680         var cog = {
26681                 xtype: 'Button',
26682                 size : 'sm',
26683                 xns: Roo.bootstrap,
26684                 glyphicon : 'cog',
26685                 //html : 'submit'
26686                 menu : {
26687                     xtype: 'Menu',
26688                     xns: Roo.bootstrap,
26689                     items:  []
26690                 }
26691         };
26692         
26693         cog.menu.items.push({
26694             xtype :'MenuItem',
26695             xns: Roo.bootstrap,
26696             html : Clean styles,
26697             tagname : f,
26698             listeners : {
26699                 click : function()
26700                 {
26701                     editorcore.insertTag(this.tagname);
26702                     editor.focus();
26703                 }
26704             }
26705             
26706         });
26707        */
26708         
26709          
26710        this.xtype = 'NavSimplebar';
26711         
26712         for(var i=0;i< children.length;i++) {
26713             
26714             this.buttons.add(this.addxtypeChild(children[i]));
26715             
26716         }
26717         
26718         editor.on('editorevent', this.updateToolbar, this);
26719     },
26720     onBtnClick : function(id)
26721     {
26722        this.editorcore.relayCmd(id);
26723        this.editorcore.focus();
26724     },
26725     
26726     /**
26727      * Protected method that will not generally be called directly. It triggers
26728      * a toolbar update by reading the markup state of the current selection in the editor.
26729      */
26730     updateToolbar: function(){
26731
26732         if(!this.editorcore.activated){
26733             this.editor.onFirstFocus(); // is this neeed?
26734             return;
26735         }
26736
26737         var btns = this.buttons; 
26738         var doc = this.editorcore.doc;
26739         btns.get('bold').setActive(doc.queryCommandState('bold'));
26740         btns.get('italic').setActive(doc.queryCommandState('italic'));
26741         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26742         
26743         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26744         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26745         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26746         
26747         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26748         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26749          /*
26750         
26751         var ans = this.editorcore.getAllAncestors();
26752         if (this.formatCombo) {
26753             
26754             
26755             var store = this.formatCombo.store;
26756             this.formatCombo.setValue("");
26757             for (var i =0; i < ans.length;i++) {
26758                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26759                     // select it..
26760                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26761                     break;
26762                 }
26763             }
26764         }
26765         
26766         
26767         
26768         // hides menus... - so this cant be on a menu...
26769         Roo.bootstrap.MenuMgr.hideAll();
26770         */
26771         Roo.bootstrap.MenuMgr.hideAll();
26772         //this.editorsyncValue();
26773     },
26774     onFirstFocus: function() {
26775         this.buttons.each(function(item){
26776            item.enable();
26777         });
26778     },
26779     toggleSourceEdit : function(sourceEditMode){
26780         
26781           
26782         if(sourceEditMode){
26783             Roo.log("disabling buttons");
26784            this.buttons.each( function(item){
26785                 if(item.cmd != 'pencil'){
26786                     item.disable();
26787                 }
26788             });
26789           
26790         }else{
26791             Roo.log("enabling buttons");
26792             if(this.editorcore.initialized){
26793                 this.buttons.each( function(item){
26794                     item.enable();
26795                 });
26796             }
26797             
26798         }
26799         Roo.log("calling toggole on editor");
26800         // tell the editor that it's been pressed..
26801         this.editor.toggleSourceEdit(sourceEditMode);
26802        
26803     }
26804 });
26805
26806
26807
26808
26809  
26810 /*
26811  * - LGPL
26812  */
26813
26814 /**
26815  * @class Roo.bootstrap.Markdown
26816  * @extends Roo.bootstrap.TextArea
26817  * Bootstrap Showdown editable area
26818  * @cfg {string} content
26819  * 
26820  * @constructor
26821  * Create a new Showdown
26822  */
26823
26824 Roo.bootstrap.Markdown = function(config){
26825     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26826    
26827 };
26828
26829 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26830     
26831     editing :false,
26832     
26833     initEvents : function()
26834     {
26835         
26836         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26837         this.markdownEl = this.el.createChild({
26838             cls : 'roo-markdown-area'
26839         });
26840         this.inputEl().addClass('d-none');
26841         if (this.getValue() == '') {
26842             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26843             
26844         } else {
26845             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26846         }
26847         this.markdownEl.on('click', this.toggleTextEdit, this);
26848         this.on('blur', this.toggleTextEdit, this);
26849         this.on('specialkey', this.resizeTextArea, this);
26850     },
26851     
26852     toggleTextEdit : function()
26853     {
26854         var sh = this.markdownEl.getHeight();
26855         this.inputEl().addClass('d-none');
26856         this.markdownEl.addClass('d-none');
26857         if (!this.editing) {
26858             // show editor?
26859             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26860             this.inputEl().removeClass('d-none');
26861             this.inputEl().focus();
26862             this.editing = true;
26863             return;
26864         }
26865         // show showdown...
26866         this.updateMarkdown();
26867         this.markdownEl.removeClass('d-none');
26868         this.editing = false;
26869         return;
26870     },
26871     updateMarkdown : function()
26872     {
26873         if (this.getValue() == '') {
26874             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26875             return;
26876         }
26877  
26878         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26879     },
26880     
26881     resizeTextArea: function () {
26882         
26883         var sh = 100;
26884         Roo.log([sh, this.getValue().split("\n").length * 30]);
26885         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26886     },
26887     setValue : function(val)
26888     {
26889         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26890         if (!this.editing) {
26891             this.updateMarkdown();
26892         }
26893         
26894     },
26895     focus : function()
26896     {
26897         if (!this.editing) {
26898             this.toggleTextEdit();
26899         }
26900         
26901     }
26902
26903
26904 });
26905 /**
26906  * @class Roo.bootstrap.Table.AbstractSelectionModel
26907  * @extends Roo.util.Observable
26908  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26909  * implemented by descendant classes.  This class should not be directly instantiated.
26910  * @constructor
26911  */
26912 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26913     this.locked = false;
26914     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26915 };
26916
26917
26918 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26919     /** @ignore Called by the grid automatically. Do not call directly. */
26920     init : function(grid){
26921         this.grid = grid;
26922         this.initEvents();
26923     },
26924
26925     /**
26926      * Locks the selections.
26927      */
26928     lock : function(){
26929         this.locked = true;
26930     },
26931
26932     /**
26933      * Unlocks the selections.
26934      */
26935     unlock : function(){
26936         this.locked = false;
26937     },
26938
26939     /**
26940      * Returns true if the selections are locked.
26941      * @return {Boolean}
26942      */
26943     isLocked : function(){
26944         return this.locked;
26945     },
26946     
26947     
26948     initEvents : function ()
26949     {
26950         
26951     }
26952 });
26953 /**
26954  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26955  * @class Roo.bootstrap.Table.RowSelectionModel
26956  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26957  * It supports multiple selections and keyboard selection/navigation. 
26958  * @constructor
26959  * @param {Object} config
26960  */
26961
26962 Roo.bootstrap.Table.RowSelectionModel = function(config){
26963     Roo.apply(this, config);
26964     this.selections = new Roo.util.MixedCollection(false, function(o){
26965         return o.id;
26966     });
26967
26968     this.last = false;
26969     this.lastActive = false;
26970
26971     this.addEvents({
26972         /**
26973              * @event selectionchange
26974              * Fires when the selection changes
26975              * @param {SelectionModel} this
26976              */
26977             "selectionchange" : true,
26978         /**
26979              * @event afterselectionchange
26980              * Fires after the selection changes (eg. by key press or clicking)
26981              * @param {SelectionModel} this
26982              */
26983             "afterselectionchange" : true,
26984         /**
26985              * @event beforerowselect
26986              * Fires when a row is selected being selected, return false to cancel.
26987              * @param {SelectionModel} this
26988              * @param {Number} rowIndex The selected index
26989              * @param {Boolean} keepExisting False if other selections will be cleared
26990              */
26991             "beforerowselect" : true,
26992         /**
26993              * @event rowselect
26994              * Fires when a row is selected.
26995              * @param {SelectionModel} this
26996              * @param {Number} rowIndex The selected index
26997              * @param {Roo.data.Record} r The record
26998              */
26999             "rowselect" : true,
27000         /**
27001              * @event rowdeselect
27002              * Fires when a row is deselected.
27003              * @param {SelectionModel} this
27004              * @param {Number} rowIndex The selected index
27005              */
27006         "rowdeselect" : true
27007     });
27008     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27009     this.locked = false;
27010  };
27011
27012 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27013     /**
27014      * @cfg {Boolean} singleSelect
27015      * True to allow selection of only one row at a time (defaults to false)
27016      */
27017     singleSelect : false,
27018
27019     // private
27020     initEvents : function()
27021     {
27022
27023         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27024         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27025         //}else{ // allow click to work like normal
27026          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27027         //}
27028         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27029         this.grid.on("rowclick", this.handleMouseDown, this);
27030         
27031         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27032             "up" : function(e){
27033                 if(!e.shiftKey){
27034                     this.selectPrevious(e.shiftKey);
27035                 }else if(this.last !== false && this.lastActive !== false){
27036                     var last = this.last;
27037                     this.selectRange(this.last,  this.lastActive-1);
27038                     this.grid.getView().focusRow(this.lastActive);
27039                     if(last !== false){
27040                         this.last = last;
27041                     }
27042                 }else{
27043                     this.selectFirstRow();
27044                 }
27045                 this.fireEvent("afterselectionchange", this);
27046             },
27047             "down" : function(e){
27048                 if(!e.shiftKey){
27049                     this.selectNext(e.shiftKey);
27050                 }else if(this.last !== false && this.lastActive !== false){
27051                     var last = this.last;
27052                     this.selectRange(this.last,  this.lastActive+1);
27053                     this.grid.getView().focusRow(this.lastActive);
27054                     if(last !== false){
27055                         this.last = last;
27056                     }
27057                 }else{
27058                     this.selectFirstRow();
27059                 }
27060                 this.fireEvent("afterselectionchange", this);
27061             },
27062             scope: this
27063         });
27064         this.grid.store.on('load', function(){
27065             this.selections.clear();
27066         },this);
27067         /*
27068         var view = this.grid.view;
27069         view.on("refresh", this.onRefresh, this);
27070         view.on("rowupdated", this.onRowUpdated, this);
27071         view.on("rowremoved", this.onRemove, this);
27072         */
27073     },
27074
27075     // private
27076     onRefresh : function()
27077     {
27078         var ds = this.grid.store, i, v = this.grid.view;
27079         var s = this.selections;
27080         s.each(function(r){
27081             if((i = ds.indexOfId(r.id)) != -1){
27082                 v.onRowSelect(i);
27083             }else{
27084                 s.remove(r);
27085             }
27086         });
27087     },
27088
27089     // private
27090     onRemove : function(v, index, r){
27091         this.selections.remove(r);
27092     },
27093
27094     // private
27095     onRowUpdated : function(v, index, r){
27096         if(this.isSelected(r)){
27097             v.onRowSelect(index);
27098         }
27099     },
27100
27101     /**
27102      * Select records.
27103      * @param {Array} records The records to select
27104      * @param {Boolean} keepExisting (optional) True to keep existing selections
27105      */
27106     selectRecords : function(records, keepExisting)
27107     {
27108         if(!keepExisting){
27109             this.clearSelections();
27110         }
27111             var ds = this.grid.store;
27112         for(var i = 0, len = records.length; i < len; i++){
27113             this.selectRow(ds.indexOf(records[i]), true);
27114         }
27115     },
27116
27117     /**
27118      * Gets the number of selected rows.
27119      * @return {Number}
27120      */
27121     getCount : function(){
27122         return this.selections.length;
27123     },
27124
27125     /**
27126      * Selects the first row in the grid.
27127      */
27128     selectFirstRow : function(){
27129         this.selectRow(0);
27130     },
27131
27132     /**
27133      * Select the last row.
27134      * @param {Boolean} keepExisting (optional) True to keep existing selections
27135      */
27136     selectLastRow : function(keepExisting){
27137         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27138         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27139     },
27140
27141     /**
27142      * Selects the row immediately following the last selected row.
27143      * @param {Boolean} keepExisting (optional) True to keep existing selections
27144      */
27145     selectNext : function(keepExisting)
27146     {
27147             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27148             this.selectRow(this.last+1, keepExisting);
27149             this.grid.getView().focusRow(this.last);
27150         }
27151     },
27152
27153     /**
27154      * Selects the row that precedes the last selected row.
27155      * @param {Boolean} keepExisting (optional) True to keep existing selections
27156      */
27157     selectPrevious : function(keepExisting){
27158         if(this.last){
27159             this.selectRow(this.last-1, keepExisting);
27160             this.grid.getView().focusRow(this.last);
27161         }
27162     },
27163
27164     /**
27165      * Returns the selected records
27166      * @return {Array} Array of selected records
27167      */
27168     getSelections : function(){
27169         return [].concat(this.selections.items);
27170     },
27171
27172     /**
27173      * Returns the first selected record.
27174      * @return {Record}
27175      */
27176     getSelected : function(){
27177         return this.selections.itemAt(0);
27178     },
27179
27180
27181     /**
27182      * Clears all selections.
27183      */
27184     clearSelections : function(fast)
27185     {
27186         if(this.locked) {
27187             return;
27188         }
27189         if(fast !== true){
27190                 var ds = this.grid.store;
27191             var s = this.selections;
27192             s.each(function(r){
27193                 this.deselectRow(ds.indexOfId(r.id));
27194             }, this);
27195             s.clear();
27196         }else{
27197             this.selections.clear();
27198         }
27199         this.last = false;
27200     },
27201
27202
27203     /**
27204      * Selects all rows.
27205      */
27206     selectAll : function(){
27207         if(this.locked) {
27208             return;
27209         }
27210         this.selections.clear();
27211         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27212             this.selectRow(i, true);
27213         }
27214     },
27215
27216     /**
27217      * Returns True if there is a selection.
27218      * @return {Boolean}
27219      */
27220     hasSelection : function(){
27221         return this.selections.length > 0;
27222     },
27223
27224     /**
27225      * Returns True if the specified row is selected.
27226      * @param {Number/Record} record The record or index of the record to check
27227      * @return {Boolean}
27228      */
27229     isSelected : function(index){
27230             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27231         return (r && this.selections.key(r.id) ? true : false);
27232     },
27233
27234     /**
27235      * Returns True if the specified record id is selected.
27236      * @param {String} id The id of record to check
27237      * @return {Boolean}
27238      */
27239     isIdSelected : function(id){
27240         return (this.selections.key(id) ? true : false);
27241     },
27242
27243
27244     // private
27245     handleMouseDBClick : function(e, t){
27246         
27247     },
27248     // private
27249     handleMouseDown : function(e, t)
27250     {
27251             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27252         if(this.isLocked() || rowIndex < 0 ){
27253             return;
27254         };
27255         if(e.shiftKey && this.last !== false){
27256             var last = this.last;
27257             this.selectRange(last, rowIndex, e.ctrlKey);
27258             this.last = last; // reset the last
27259             t.focus();
27260     
27261         }else{
27262             var isSelected = this.isSelected(rowIndex);
27263             //Roo.log("select row:" + rowIndex);
27264             if(isSelected){
27265                 this.deselectRow(rowIndex);
27266             } else {
27267                         this.selectRow(rowIndex, true);
27268             }
27269     
27270             /*
27271                 if(e.button !== 0 && isSelected){
27272                 alert('rowIndex 2: ' + rowIndex);
27273                     view.focusRow(rowIndex);
27274                 }else if(e.ctrlKey && isSelected){
27275                     this.deselectRow(rowIndex);
27276                 }else if(!isSelected){
27277                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27278                     view.focusRow(rowIndex);
27279                 }
27280             */
27281         }
27282         this.fireEvent("afterselectionchange", this);
27283     },
27284     // private
27285     handleDragableRowClick :  function(grid, rowIndex, e) 
27286     {
27287         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27288             this.selectRow(rowIndex, false);
27289             grid.view.focusRow(rowIndex);
27290              this.fireEvent("afterselectionchange", this);
27291         }
27292     },
27293     
27294     /**
27295      * Selects multiple rows.
27296      * @param {Array} rows Array of the indexes of the row to select
27297      * @param {Boolean} keepExisting (optional) True to keep existing selections
27298      */
27299     selectRows : function(rows, keepExisting){
27300         if(!keepExisting){
27301             this.clearSelections();
27302         }
27303         for(var i = 0, len = rows.length; i < len; i++){
27304             this.selectRow(rows[i], true);
27305         }
27306     },
27307
27308     /**
27309      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27310      * @param {Number} startRow The index of the first row in the range
27311      * @param {Number} endRow The index of the last row in the range
27312      * @param {Boolean} keepExisting (optional) True to retain existing selections
27313      */
27314     selectRange : function(startRow, endRow, keepExisting){
27315         if(this.locked) {
27316             return;
27317         }
27318         if(!keepExisting){
27319             this.clearSelections();
27320         }
27321         if(startRow <= endRow){
27322             for(var i = startRow; i <= endRow; i++){
27323                 this.selectRow(i, true);
27324             }
27325         }else{
27326             for(var i = startRow; i >= endRow; i--){
27327                 this.selectRow(i, true);
27328             }
27329         }
27330     },
27331
27332     /**
27333      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27334      * @param {Number} startRow The index of the first row in the range
27335      * @param {Number} endRow The index of the last row in the range
27336      */
27337     deselectRange : function(startRow, endRow, preventViewNotify){
27338         if(this.locked) {
27339             return;
27340         }
27341         for(var i = startRow; i <= endRow; i++){
27342             this.deselectRow(i, preventViewNotify);
27343         }
27344     },
27345
27346     /**
27347      * Selects a row.
27348      * @param {Number} row The index of the row to select
27349      * @param {Boolean} keepExisting (optional) True to keep existing selections
27350      */
27351     selectRow : function(index, keepExisting, preventViewNotify)
27352     {
27353             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27354             return;
27355         }
27356         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27357             if(!keepExisting || this.singleSelect){
27358                 this.clearSelections();
27359             }
27360             
27361             var r = this.grid.store.getAt(index);
27362             //console.log('selectRow - record id :' + r.id);
27363             
27364             this.selections.add(r);
27365             this.last = this.lastActive = index;
27366             if(!preventViewNotify){
27367                 var proxy = new Roo.Element(
27368                                 this.grid.getRowDom(index)
27369                 );
27370                 proxy.addClass('bg-info info');
27371             }
27372             this.fireEvent("rowselect", this, index, r);
27373             this.fireEvent("selectionchange", this);
27374         }
27375     },
27376
27377     /**
27378      * Deselects a row.
27379      * @param {Number} row The index of the row to deselect
27380      */
27381     deselectRow : function(index, preventViewNotify)
27382     {
27383         if(this.locked) {
27384             return;
27385         }
27386         if(this.last == index){
27387             this.last = false;
27388         }
27389         if(this.lastActive == index){
27390             this.lastActive = false;
27391         }
27392         
27393         var r = this.grid.store.getAt(index);
27394         if (!r) {
27395             return;
27396         }
27397         
27398         this.selections.remove(r);
27399         //.console.log('deselectRow - record id :' + r.id);
27400         if(!preventViewNotify){
27401         
27402             var proxy = new Roo.Element(
27403                 this.grid.getRowDom(index)
27404             );
27405             proxy.removeClass('bg-info info');
27406         }
27407         this.fireEvent("rowdeselect", this, index);
27408         this.fireEvent("selectionchange", this);
27409     },
27410
27411     // private
27412     restoreLast : function(){
27413         if(this._last){
27414             this.last = this._last;
27415         }
27416     },
27417
27418     // private
27419     acceptsNav : function(row, col, cm){
27420         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27421     },
27422
27423     // private
27424     onEditorKey : function(field, e){
27425         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27426         if(k == e.TAB){
27427             e.stopEvent();
27428             ed.completeEdit();
27429             if(e.shiftKey){
27430                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27431             }else{
27432                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27433             }
27434         }else if(k == e.ENTER && !e.ctrlKey){
27435             e.stopEvent();
27436             ed.completeEdit();
27437             if(e.shiftKey){
27438                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27439             }else{
27440                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27441             }
27442         }else if(k == e.ESC){
27443             ed.cancelEdit();
27444         }
27445         if(newCell){
27446             g.startEditing(newCell[0], newCell[1]);
27447         }
27448     }
27449 });
27450 /*
27451  * Based on:
27452  * Ext JS Library 1.1.1
27453  * Copyright(c) 2006-2007, Ext JS, LLC.
27454  *
27455  * Originally Released Under LGPL - original licence link has changed is not relivant.
27456  *
27457  * Fork - LGPL
27458  * <script type="text/javascript">
27459  */
27460  
27461 /**
27462  * @class Roo.bootstrap.PagingToolbar
27463  * @extends Roo.bootstrap.NavSimplebar
27464  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27465  * @constructor
27466  * Create a new PagingToolbar
27467  * @param {Object} config The config object
27468  * @param {Roo.data.Store} store
27469  */
27470 Roo.bootstrap.PagingToolbar = function(config)
27471 {
27472     // old args format still supported... - xtype is prefered..
27473         // created from xtype...
27474     
27475     this.ds = config.dataSource;
27476     
27477     if (config.store && !this.ds) {
27478         this.store= Roo.factory(config.store, Roo.data);
27479         this.ds = this.store;
27480         this.ds.xmodule = this.xmodule || false;
27481     }
27482     
27483     this.toolbarItems = [];
27484     if (config.items) {
27485         this.toolbarItems = config.items;
27486     }
27487     
27488     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27489     
27490     this.cursor = 0;
27491     
27492     if (this.ds) { 
27493         this.bind(this.ds);
27494     }
27495     
27496     if (Roo.bootstrap.version == 4) {
27497         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27498     } else {
27499         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27500     }
27501     
27502 };
27503
27504 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27505     /**
27506      * @cfg {Roo.data.Store} dataSource
27507      * The underlying data store providing the paged data
27508      */
27509     /**
27510      * @cfg {String/HTMLElement/Element} container
27511      * container The id or element that will contain the toolbar
27512      */
27513     /**
27514      * @cfg {Boolean} displayInfo
27515      * True to display the displayMsg (defaults to false)
27516      */
27517     /**
27518      * @cfg {Number} pageSize
27519      * The number of records to display per page (defaults to 20)
27520      */
27521     pageSize: 20,
27522     /**
27523      * @cfg {String} displayMsg
27524      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27525      */
27526     displayMsg : 'Displaying {0} - {1} of {2}',
27527     /**
27528      * @cfg {String} emptyMsg
27529      * The message to display when no records are found (defaults to "No data to display")
27530      */
27531     emptyMsg : 'No data to display',
27532     /**
27533      * Customizable piece of the default paging text (defaults to "Page")
27534      * @type String
27535      */
27536     beforePageText : "Page",
27537     /**
27538      * Customizable piece of the default paging text (defaults to "of %0")
27539      * @type String
27540      */
27541     afterPageText : "of {0}",
27542     /**
27543      * Customizable piece of the default paging text (defaults to "First Page")
27544      * @type String
27545      */
27546     firstText : "First Page",
27547     /**
27548      * Customizable piece of the default paging text (defaults to "Previous Page")
27549      * @type String
27550      */
27551     prevText : "Previous Page",
27552     /**
27553      * Customizable piece of the default paging text (defaults to "Next Page")
27554      * @type String
27555      */
27556     nextText : "Next Page",
27557     /**
27558      * Customizable piece of the default paging text (defaults to "Last Page")
27559      * @type String
27560      */
27561     lastText : "Last Page",
27562     /**
27563      * Customizable piece of the default paging text (defaults to "Refresh")
27564      * @type String
27565      */
27566     refreshText : "Refresh",
27567
27568     buttons : false,
27569     // private
27570     onRender : function(ct, position) 
27571     {
27572         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27573         this.navgroup.parentId = this.id;
27574         this.navgroup.onRender(this.el, null);
27575         // add the buttons to the navgroup
27576         
27577         if(this.displayInfo){
27578             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27579             this.displayEl = this.el.select('.x-paging-info', true).first();
27580 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27581 //            this.displayEl = navel.el.select('span',true).first();
27582         }
27583         
27584         var _this = this;
27585         
27586         if(this.buttons){
27587             Roo.each(_this.buttons, function(e){ // this might need to use render????
27588                Roo.factory(e).render(_this.el);
27589             });
27590         }
27591             
27592         Roo.each(_this.toolbarItems, function(e) {
27593             _this.navgroup.addItem(e);
27594         });
27595         
27596         
27597         this.first = this.navgroup.addItem({
27598             tooltip: this.firstText,
27599             cls: "prev btn-outline-secondary",
27600             html : ' <i class="fa fa-step-backward"></i>',
27601             disabled: true,
27602             preventDefault: true,
27603             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27604         });
27605         
27606         this.prev =  this.navgroup.addItem({
27607             tooltip: this.prevText,
27608             cls: "prev btn-outline-secondary",
27609             html : ' <i class="fa fa-backward"></i>',
27610             disabled: true,
27611             preventDefault: true,
27612             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27613         });
27614     //this.addSeparator();
27615         
27616         
27617         var field = this.navgroup.addItem( {
27618             tagtype : 'span',
27619             cls : 'x-paging-position  btn-outline-secondary',
27620              disabled: true,
27621             html : this.beforePageText  +
27622                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27623                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27624          } ); //?? escaped?
27625         
27626         this.field = field.el.select('input', true).first();
27627         this.field.on("keydown", this.onPagingKeydown, this);
27628         this.field.on("focus", function(){this.dom.select();});
27629     
27630     
27631         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27632         //this.field.setHeight(18);
27633         //this.addSeparator();
27634         this.next = this.navgroup.addItem({
27635             tooltip: this.nextText,
27636             cls: "next btn-outline-secondary",
27637             html : ' <i class="fa fa-forward"></i>',
27638             disabled: true,
27639             preventDefault: true,
27640             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27641         });
27642         this.last = this.navgroup.addItem({
27643             tooltip: this.lastText,
27644             html : ' <i class="fa fa-step-forward"></i>',
27645             cls: "next btn-outline-secondary",
27646             disabled: true,
27647             preventDefault: true,
27648             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27649         });
27650     //this.addSeparator();
27651         this.loading = this.navgroup.addItem({
27652             tooltip: this.refreshText,
27653             cls: "btn-outline-secondary",
27654             html : ' <i class="fa fa-refresh"></i>',
27655             preventDefault: true,
27656             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27657         });
27658         
27659     },
27660
27661     // private
27662     updateInfo : function(){
27663         if(this.displayEl){
27664             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27665             var msg = count == 0 ?
27666                 this.emptyMsg :
27667                 String.format(
27668                     this.displayMsg,
27669                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27670                 );
27671             this.displayEl.update(msg);
27672         }
27673     },
27674
27675     // private
27676     onLoad : function(ds, r, o)
27677     {
27678         this.cursor = o.params && o.params.start ? o.params.start : 0;
27679         
27680         var d = this.getPageData(),
27681             ap = d.activePage,
27682             ps = d.pages;
27683         
27684         
27685         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27686         this.field.dom.value = ap;
27687         this.first.setDisabled(ap == 1);
27688         this.prev.setDisabled(ap == 1);
27689         this.next.setDisabled(ap == ps);
27690         this.last.setDisabled(ap == ps);
27691         this.loading.enable();
27692         this.updateInfo();
27693     },
27694
27695     // private
27696     getPageData : function(){
27697         var total = this.ds.getTotalCount();
27698         return {
27699             total : total,
27700             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27701             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27702         };
27703     },
27704
27705     // private
27706     onLoadError : function(){
27707         this.loading.enable();
27708     },
27709
27710     // private
27711     onPagingKeydown : function(e){
27712         var k = e.getKey();
27713         var d = this.getPageData();
27714         if(k == e.RETURN){
27715             var v = this.field.dom.value, pageNum;
27716             if(!v || isNaN(pageNum = parseInt(v, 10))){
27717                 this.field.dom.value = d.activePage;
27718                 return;
27719             }
27720             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27721             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27722             e.stopEvent();
27723         }
27724         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))
27725         {
27726           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27727           this.field.dom.value = pageNum;
27728           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27729           e.stopEvent();
27730         }
27731         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27732         {
27733           var v = this.field.dom.value, pageNum; 
27734           var increment = (e.shiftKey) ? 10 : 1;
27735           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27736                 increment *= -1;
27737           }
27738           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27739             this.field.dom.value = d.activePage;
27740             return;
27741           }
27742           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27743           {
27744             this.field.dom.value = parseInt(v, 10) + increment;
27745             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27746             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27747           }
27748           e.stopEvent();
27749         }
27750     },
27751
27752     // private
27753     beforeLoad : function(){
27754         if(this.loading){
27755             this.loading.disable();
27756         }
27757     },
27758
27759     // private
27760     onClick : function(which){
27761         
27762         var ds = this.ds;
27763         if (!ds) {
27764             return;
27765         }
27766         
27767         switch(which){
27768             case "first":
27769                 ds.load({params:{start: 0, limit: this.pageSize}});
27770             break;
27771             case "prev":
27772                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27773             break;
27774             case "next":
27775                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27776             break;
27777             case "last":
27778                 var total = ds.getTotalCount();
27779                 var extra = total % this.pageSize;
27780                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27781                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27782             break;
27783             case "refresh":
27784                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27785             break;
27786         }
27787     },
27788
27789     /**
27790      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27791      * @param {Roo.data.Store} store The data store to unbind
27792      */
27793     unbind : function(ds){
27794         ds.un("beforeload", this.beforeLoad, this);
27795         ds.un("load", this.onLoad, this);
27796         ds.un("loadexception", this.onLoadError, this);
27797         ds.un("remove", this.updateInfo, this);
27798         ds.un("add", this.updateInfo, this);
27799         this.ds = undefined;
27800     },
27801
27802     /**
27803      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27804      * @param {Roo.data.Store} store The data store to bind
27805      */
27806     bind : function(ds){
27807         ds.on("beforeload", this.beforeLoad, this);
27808         ds.on("load", this.onLoad, this);
27809         ds.on("loadexception", this.onLoadError, this);
27810         ds.on("remove", this.updateInfo, this);
27811         ds.on("add", this.updateInfo, this);
27812         this.ds = ds;
27813     }
27814 });/*
27815  * - LGPL
27816  *
27817  * element
27818  * 
27819  */
27820
27821 /**
27822  * @class Roo.bootstrap.MessageBar
27823  * @extends Roo.bootstrap.Component
27824  * Bootstrap MessageBar class
27825  * @cfg {String} html contents of the MessageBar
27826  * @cfg {String} weight (info | success | warning | danger) default info
27827  * @cfg {String} beforeClass insert the bar before the given class
27828  * @cfg {Boolean} closable (true | false) default false
27829  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27830  * 
27831  * @constructor
27832  * Create a new Element
27833  * @param {Object} config The config object
27834  */
27835
27836 Roo.bootstrap.MessageBar = function(config){
27837     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27838 };
27839
27840 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27841     
27842     html: '',
27843     weight: 'info',
27844     closable: false,
27845     fixed: false,
27846     beforeClass: 'bootstrap-sticky-wrap',
27847     
27848     getAutoCreate : function(){
27849         
27850         var cfg = {
27851             tag: 'div',
27852             cls: 'alert alert-dismissable alert-' + this.weight,
27853             cn: [
27854                 {
27855                     tag: 'span',
27856                     cls: 'message',
27857                     html: this.html || ''
27858                 }
27859             ]
27860         };
27861         
27862         if(this.fixed){
27863             cfg.cls += ' alert-messages-fixed';
27864         }
27865         
27866         if(this.closable){
27867             cfg.cn.push({
27868                 tag: 'button',
27869                 cls: 'close',
27870                 html: 'x'
27871             });
27872         }
27873         
27874         return cfg;
27875     },
27876     
27877     onRender : function(ct, position)
27878     {
27879         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27880         
27881         if(!this.el){
27882             var cfg = Roo.apply({},  this.getAutoCreate());
27883             cfg.id = Roo.id();
27884             
27885             if (this.cls) {
27886                 cfg.cls += ' ' + this.cls;
27887             }
27888             if (this.style) {
27889                 cfg.style = this.style;
27890             }
27891             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27892             
27893             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27894         }
27895         
27896         this.el.select('>button.close').on('click', this.hide, this);
27897         
27898     },
27899     
27900     show : function()
27901     {
27902         if (!this.rendered) {
27903             this.render();
27904         }
27905         
27906         this.el.show();
27907         
27908         this.fireEvent('show', this);
27909         
27910     },
27911     
27912     hide : function()
27913     {
27914         if (!this.rendered) {
27915             this.render();
27916         }
27917         
27918         this.el.hide();
27919         
27920         this.fireEvent('hide', this);
27921     },
27922     
27923     update : function()
27924     {
27925 //        var e = this.el.dom.firstChild;
27926 //        
27927 //        if(this.closable){
27928 //            e = e.nextSibling;
27929 //        }
27930 //        
27931 //        e.data = this.html || '';
27932
27933         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27934     }
27935    
27936 });
27937
27938  
27939
27940      /*
27941  * - LGPL
27942  *
27943  * Graph
27944  * 
27945  */
27946
27947
27948 /**
27949  * @class Roo.bootstrap.Graph
27950  * @extends Roo.bootstrap.Component
27951  * Bootstrap Graph class
27952 > Prameters
27953  -sm {number} sm 4
27954  -md {number} md 5
27955  @cfg {String} graphtype  bar | vbar | pie
27956  @cfg {number} g_x coodinator | centre x (pie)
27957  @cfg {number} g_y coodinator | centre y (pie)
27958  @cfg {number} g_r radius (pie)
27959  @cfg {number} g_height height of the chart (respected by all elements in the set)
27960  @cfg {number} g_width width of the chart (respected by all elements in the set)
27961  @cfg {Object} title The title of the chart
27962     
27963  -{Array}  values
27964  -opts (object) options for the chart 
27965      o {
27966      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27967      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27968      o vgutter (number)
27969      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.
27970      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27971      o to
27972      o stretch (boolean)
27973      o }
27974  -opts (object) options for the pie
27975      o{
27976      o cut
27977      o startAngle (number)
27978      o endAngle (number)
27979      } 
27980  *
27981  * @constructor
27982  * Create a new Input
27983  * @param {Object} config The config object
27984  */
27985
27986 Roo.bootstrap.Graph = function(config){
27987     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27988     
27989     this.addEvents({
27990         // img events
27991         /**
27992          * @event click
27993          * The img click event for the img.
27994          * @param {Roo.EventObject} e
27995          */
27996         "click" : true
27997     });
27998 };
27999
28000 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28001     
28002     sm: 4,
28003     md: 5,
28004     graphtype: 'bar',
28005     g_height: 250,
28006     g_width: 400,
28007     g_x: 50,
28008     g_y: 50,
28009     g_r: 30,
28010     opts:{
28011         //g_colors: this.colors,
28012         g_type: 'soft',
28013         g_gutter: '20%'
28014
28015     },
28016     title : false,
28017
28018     getAutoCreate : function(){
28019         
28020         var cfg = {
28021             tag: 'div',
28022             html : null
28023         };
28024         
28025         
28026         return  cfg;
28027     },
28028
28029     onRender : function(ct,position){
28030         
28031         
28032         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28033         
28034         if (typeof(Raphael) == 'undefined') {
28035             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28036             return;
28037         }
28038         
28039         this.raphael = Raphael(this.el.dom);
28040         
28041                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28042                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28043                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28044                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28045                 /*
28046                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28047                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28048                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28049                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28050                 
28051                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28052                 r.barchart(330, 10, 300, 220, data1);
28053                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28054                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28055                 */
28056                 
28057                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28058                 // r.barchart(30, 30, 560, 250,  xdata, {
28059                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28060                 //     axis : "0 0 1 1",
28061                 //     axisxlabels :  xdata
28062                 //     //yvalues : cols,
28063                    
28064                 // });
28065 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28066 //        
28067 //        this.load(null,xdata,{
28068 //                axis : "0 0 1 1",
28069 //                axisxlabels :  xdata
28070 //                });
28071
28072     },
28073
28074     load : function(graphtype,xdata,opts)
28075     {
28076         this.raphael.clear();
28077         if(!graphtype) {
28078             graphtype = this.graphtype;
28079         }
28080         if(!opts){
28081             opts = this.opts;
28082         }
28083         var r = this.raphael,
28084             fin = function () {
28085                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28086             },
28087             fout = function () {
28088                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28089             },
28090             pfin = function() {
28091                 this.sector.stop();
28092                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28093
28094                 if (this.label) {
28095                     this.label[0].stop();
28096                     this.label[0].attr({ r: 7.5 });
28097                     this.label[1].attr({ "font-weight": 800 });
28098                 }
28099             },
28100             pfout = function() {
28101                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28102
28103                 if (this.label) {
28104                     this.label[0].animate({ r: 5 }, 500, "bounce");
28105                     this.label[1].attr({ "font-weight": 400 });
28106                 }
28107             };
28108
28109         switch(graphtype){
28110             case 'bar':
28111                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28112                 break;
28113             case 'hbar':
28114                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28115                 break;
28116             case 'pie':
28117 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28118 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28119 //            
28120                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28121                 
28122                 break;
28123
28124         }
28125         
28126         if(this.title){
28127             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28128         }
28129         
28130     },
28131     
28132     setTitle: function(o)
28133     {
28134         this.title = o;
28135     },
28136     
28137     initEvents: function() {
28138         
28139         if(!this.href){
28140             this.el.on('click', this.onClick, this);
28141         }
28142     },
28143     
28144     onClick : function(e)
28145     {
28146         Roo.log('img onclick');
28147         this.fireEvent('click', this, e);
28148     }
28149    
28150 });
28151
28152  
28153 /*
28154  * - LGPL
28155  *
28156  * numberBox
28157  * 
28158  */
28159 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28160
28161 /**
28162  * @class Roo.bootstrap.dash.NumberBox
28163  * @extends Roo.bootstrap.Component
28164  * Bootstrap NumberBox class
28165  * @cfg {String} headline Box headline
28166  * @cfg {String} content Box content
28167  * @cfg {String} icon Box icon
28168  * @cfg {String} footer Footer text
28169  * @cfg {String} fhref Footer href
28170  * 
28171  * @constructor
28172  * Create a new NumberBox
28173  * @param {Object} config The config object
28174  */
28175
28176
28177 Roo.bootstrap.dash.NumberBox = function(config){
28178     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28179     
28180 };
28181
28182 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28183     
28184     headline : '',
28185     content : '',
28186     icon : '',
28187     footer : '',
28188     fhref : '',
28189     ficon : '',
28190     
28191     getAutoCreate : function(){
28192         
28193         var cfg = {
28194             tag : 'div',
28195             cls : 'small-box ',
28196             cn : [
28197                 {
28198                     tag : 'div',
28199                     cls : 'inner',
28200                     cn :[
28201                         {
28202                             tag : 'h3',
28203                             cls : 'roo-headline',
28204                             html : this.headline
28205                         },
28206                         {
28207                             tag : 'p',
28208                             cls : 'roo-content',
28209                             html : this.content
28210                         }
28211                     ]
28212                 }
28213             ]
28214         };
28215         
28216         if(this.icon){
28217             cfg.cn.push({
28218                 tag : 'div',
28219                 cls : 'icon',
28220                 cn :[
28221                     {
28222                         tag : 'i',
28223                         cls : 'ion ' + this.icon
28224                     }
28225                 ]
28226             });
28227         }
28228         
28229         if(this.footer){
28230             var footer = {
28231                 tag : 'a',
28232                 cls : 'small-box-footer',
28233                 href : this.fhref || '#',
28234                 html : this.footer
28235             };
28236             
28237             cfg.cn.push(footer);
28238             
28239         }
28240         
28241         return  cfg;
28242     },
28243
28244     onRender : function(ct,position){
28245         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28246
28247
28248        
28249                 
28250     },
28251
28252     setHeadline: function (value)
28253     {
28254         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28255     },
28256     
28257     setFooter: function (value, href)
28258     {
28259         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28260         
28261         if(href){
28262             this.el.select('a.small-box-footer',true).first().attr('href', href);
28263         }
28264         
28265     },
28266
28267     setContent: function (value)
28268     {
28269         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28270     },
28271
28272     initEvents: function() 
28273     {   
28274         
28275     }
28276     
28277 });
28278
28279  
28280 /*
28281  * - LGPL
28282  *
28283  * TabBox
28284  * 
28285  */
28286 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28287
28288 /**
28289  * @class Roo.bootstrap.dash.TabBox
28290  * @extends Roo.bootstrap.Component
28291  * Bootstrap TabBox class
28292  * @cfg {String} title Title of the TabBox
28293  * @cfg {String} icon Icon of the TabBox
28294  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28295  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28296  * 
28297  * @constructor
28298  * Create a new TabBox
28299  * @param {Object} config The config object
28300  */
28301
28302
28303 Roo.bootstrap.dash.TabBox = function(config){
28304     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28305     this.addEvents({
28306         // raw events
28307         /**
28308          * @event addpane
28309          * When a pane is added
28310          * @param {Roo.bootstrap.dash.TabPane} pane
28311          */
28312         "addpane" : true,
28313         /**
28314          * @event activatepane
28315          * When a pane is activated
28316          * @param {Roo.bootstrap.dash.TabPane} pane
28317          */
28318         "activatepane" : true
28319         
28320          
28321     });
28322     
28323     this.panes = [];
28324 };
28325
28326 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28327
28328     title : '',
28329     icon : false,
28330     showtabs : true,
28331     tabScrollable : false,
28332     
28333     getChildContainer : function()
28334     {
28335         return this.el.select('.tab-content', true).first();
28336     },
28337     
28338     getAutoCreate : function(){
28339         
28340         var header = {
28341             tag: 'li',
28342             cls: 'pull-left header',
28343             html: this.title,
28344             cn : []
28345         };
28346         
28347         if(this.icon){
28348             header.cn.push({
28349                 tag: 'i',
28350                 cls: 'fa ' + this.icon
28351             });
28352         }
28353         
28354         var h = {
28355             tag: 'ul',
28356             cls: 'nav nav-tabs pull-right',
28357             cn: [
28358                 header
28359             ]
28360         };
28361         
28362         if(this.tabScrollable){
28363             h = {
28364                 tag: 'div',
28365                 cls: 'tab-header',
28366                 cn: [
28367                     {
28368                         tag: 'ul',
28369                         cls: 'nav nav-tabs pull-right',
28370                         cn: [
28371                             header
28372                         ]
28373                     }
28374                 ]
28375             };
28376         }
28377         
28378         var cfg = {
28379             tag: 'div',
28380             cls: 'nav-tabs-custom',
28381             cn: [
28382                 h,
28383                 {
28384                     tag: 'div',
28385                     cls: 'tab-content no-padding',
28386                     cn: []
28387                 }
28388             ]
28389         };
28390
28391         return  cfg;
28392     },
28393     initEvents : function()
28394     {
28395         //Roo.log('add add pane handler');
28396         this.on('addpane', this.onAddPane, this);
28397     },
28398      /**
28399      * Updates the box title
28400      * @param {String} html to set the title to.
28401      */
28402     setTitle : function(value)
28403     {
28404         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28405     },
28406     onAddPane : function(pane)
28407     {
28408         this.panes.push(pane);
28409         //Roo.log('addpane');
28410         //Roo.log(pane);
28411         // tabs are rendere left to right..
28412         if(!this.showtabs){
28413             return;
28414         }
28415         
28416         var ctr = this.el.select('.nav-tabs', true).first();
28417          
28418          
28419         var existing = ctr.select('.nav-tab',true);
28420         var qty = existing.getCount();;
28421         
28422         
28423         var tab = ctr.createChild({
28424             tag : 'li',
28425             cls : 'nav-tab' + (qty ? '' : ' active'),
28426             cn : [
28427                 {
28428                     tag : 'a',
28429                     href:'#',
28430                     html : pane.title
28431                 }
28432             ]
28433         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28434         pane.tab = tab;
28435         
28436         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28437         if (!qty) {
28438             pane.el.addClass('active');
28439         }
28440         
28441                 
28442     },
28443     onTabClick : function(ev,un,ob,pane)
28444     {
28445         //Roo.log('tab - prev default');
28446         ev.preventDefault();
28447         
28448         
28449         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28450         pane.tab.addClass('active');
28451         //Roo.log(pane.title);
28452         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28453         // technically we should have a deactivate event.. but maybe add later.
28454         // and it should not de-activate the selected tab...
28455         this.fireEvent('activatepane', pane);
28456         pane.el.addClass('active');
28457         pane.fireEvent('activate');
28458         
28459         
28460     },
28461     
28462     getActivePane : function()
28463     {
28464         var r = false;
28465         Roo.each(this.panes, function(p) {
28466             if(p.el.hasClass('active')){
28467                 r = p;
28468                 return false;
28469             }
28470             
28471             return;
28472         });
28473         
28474         return r;
28475     }
28476     
28477     
28478 });
28479
28480  
28481 /*
28482  * - LGPL
28483  *
28484  * Tab pane
28485  * 
28486  */
28487 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28488 /**
28489  * @class Roo.bootstrap.TabPane
28490  * @extends Roo.bootstrap.Component
28491  * Bootstrap TabPane class
28492  * @cfg {Boolean} active (false | true) Default false
28493  * @cfg {String} title title of panel
28494
28495  * 
28496  * @constructor
28497  * Create a new TabPane
28498  * @param {Object} config The config object
28499  */
28500
28501 Roo.bootstrap.dash.TabPane = function(config){
28502     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28503     
28504     this.addEvents({
28505         // raw events
28506         /**
28507          * @event activate
28508          * When a pane is activated
28509          * @param {Roo.bootstrap.dash.TabPane} pane
28510          */
28511         "activate" : true
28512          
28513     });
28514 };
28515
28516 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28517     
28518     active : false,
28519     title : '',
28520     
28521     // the tabBox that this is attached to.
28522     tab : false,
28523      
28524     getAutoCreate : function() 
28525     {
28526         var cfg = {
28527             tag: 'div',
28528             cls: 'tab-pane'
28529         };
28530         
28531         if(this.active){
28532             cfg.cls += ' active';
28533         }
28534         
28535         return cfg;
28536     },
28537     initEvents  : function()
28538     {
28539         //Roo.log('trigger add pane handler');
28540         this.parent().fireEvent('addpane', this)
28541     },
28542     
28543      /**
28544      * Updates the tab title 
28545      * @param {String} html to set the title to.
28546      */
28547     setTitle: function(str)
28548     {
28549         if (!this.tab) {
28550             return;
28551         }
28552         this.title = str;
28553         this.tab.select('a', true).first().dom.innerHTML = str;
28554         
28555     }
28556     
28557     
28558     
28559 });
28560
28561  
28562
28563
28564  /*
28565  * - LGPL
28566  *
28567  * menu
28568  * 
28569  */
28570 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28571
28572 /**
28573  * @class Roo.bootstrap.menu.Menu
28574  * @extends Roo.bootstrap.Component
28575  * Bootstrap Menu class - container for Menu
28576  * @cfg {String} html Text of the menu
28577  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28578  * @cfg {String} icon Font awesome icon
28579  * @cfg {String} pos Menu align to (top | bottom) default bottom
28580  * 
28581  * 
28582  * @constructor
28583  * Create a new Menu
28584  * @param {Object} config The config object
28585  */
28586
28587
28588 Roo.bootstrap.menu.Menu = function(config){
28589     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28590     
28591     this.addEvents({
28592         /**
28593          * @event beforeshow
28594          * Fires before this menu is displayed
28595          * @param {Roo.bootstrap.menu.Menu} this
28596          */
28597         beforeshow : true,
28598         /**
28599          * @event beforehide
28600          * Fires before this menu is hidden
28601          * @param {Roo.bootstrap.menu.Menu} this
28602          */
28603         beforehide : true,
28604         /**
28605          * @event show
28606          * Fires after this menu is displayed
28607          * @param {Roo.bootstrap.menu.Menu} this
28608          */
28609         show : true,
28610         /**
28611          * @event hide
28612          * Fires after this menu is hidden
28613          * @param {Roo.bootstrap.menu.Menu} this
28614          */
28615         hide : true,
28616         /**
28617          * @event click
28618          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28619          * @param {Roo.bootstrap.menu.Menu} this
28620          * @param {Roo.EventObject} e
28621          */
28622         click : true
28623     });
28624     
28625 };
28626
28627 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28628     
28629     submenu : false,
28630     html : '',
28631     weight : 'default',
28632     icon : false,
28633     pos : 'bottom',
28634     
28635     
28636     getChildContainer : function() {
28637         if(this.isSubMenu){
28638             return this.el;
28639         }
28640         
28641         return this.el.select('ul.dropdown-menu', true).first();  
28642     },
28643     
28644     getAutoCreate : function()
28645     {
28646         var text = [
28647             {
28648                 tag : 'span',
28649                 cls : 'roo-menu-text',
28650                 html : this.html
28651             }
28652         ];
28653         
28654         if(this.icon){
28655             text.unshift({
28656                 tag : 'i',
28657                 cls : 'fa ' + this.icon
28658             })
28659         }
28660         
28661         
28662         var cfg = {
28663             tag : 'div',
28664             cls : 'btn-group',
28665             cn : [
28666                 {
28667                     tag : 'button',
28668                     cls : 'dropdown-button btn btn-' + this.weight,
28669                     cn : text
28670                 },
28671                 {
28672                     tag : 'button',
28673                     cls : 'dropdown-toggle btn btn-' + this.weight,
28674                     cn : [
28675                         {
28676                             tag : 'span',
28677                             cls : 'caret'
28678                         }
28679                     ]
28680                 },
28681                 {
28682                     tag : 'ul',
28683                     cls : 'dropdown-menu'
28684                 }
28685             ]
28686             
28687         };
28688         
28689         if(this.pos == 'top'){
28690             cfg.cls += ' dropup';
28691         }
28692         
28693         if(this.isSubMenu){
28694             cfg = {
28695                 tag : 'ul',
28696                 cls : 'dropdown-menu'
28697             }
28698         }
28699         
28700         return cfg;
28701     },
28702     
28703     onRender : function(ct, position)
28704     {
28705         this.isSubMenu = ct.hasClass('dropdown-submenu');
28706         
28707         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28708     },
28709     
28710     initEvents : function() 
28711     {
28712         if(this.isSubMenu){
28713             return;
28714         }
28715         
28716         this.hidden = true;
28717         
28718         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28719         this.triggerEl.on('click', this.onTriggerPress, this);
28720         
28721         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28722         this.buttonEl.on('click', this.onClick, this);
28723         
28724     },
28725     
28726     list : function()
28727     {
28728         if(this.isSubMenu){
28729             return this.el;
28730         }
28731         
28732         return this.el.select('ul.dropdown-menu', true).first();
28733     },
28734     
28735     onClick : function(e)
28736     {
28737         this.fireEvent("click", this, e);
28738     },
28739     
28740     onTriggerPress  : function(e)
28741     {   
28742         if (this.isVisible()) {
28743             this.hide();
28744         } else {
28745             this.show();
28746         }
28747     },
28748     
28749     isVisible : function(){
28750         return !this.hidden;
28751     },
28752     
28753     show : function()
28754     {
28755         this.fireEvent("beforeshow", this);
28756         
28757         this.hidden = false;
28758         this.el.addClass('open');
28759         
28760         Roo.get(document).on("mouseup", this.onMouseUp, this);
28761         
28762         this.fireEvent("show", this);
28763         
28764         
28765     },
28766     
28767     hide : function()
28768     {
28769         this.fireEvent("beforehide", this);
28770         
28771         this.hidden = true;
28772         this.el.removeClass('open');
28773         
28774         Roo.get(document).un("mouseup", this.onMouseUp);
28775         
28776         this.fireEvent("hide", this);
28777     },
28778     
28779     onMouseUp : function()
28780     {
28781         this.hide();
28782     }
28783     
28784 });
28785
28786  
28787  /*
28788  * - LGPL
28789  *
28790  * menu item
28791  * 
28792  */
28793 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28794
28795 /**
28796  * @class Roo.bootstrap.menu.Item
28797  * @extends Roo.bootstrap.Component
28798  * Bootstrap MenuItem class
28799  * @cfg {Boolean} submenu (true | false) default false
28800  * @cfg {String} html text of the item
28801  * @cfg {String} href the link
28802  * @cfg {Boolean} disable (true | false) default false
28803  * @cfg {Boolean} preventDefault (true | false) default true
28804  * @cfg {String} icon Font awesome icon
28805  * @cfg {String} pos Submenu align to (left | right) default right 
28806  * 
28807  * 
28808  * @constructor
28809  * Create a new Item
28810  * @param {Object} config The config object
28811  */
28812
28813
28814 Roo.bootstrap.menu.Item = function(config){
28815     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28816     this.addEvents({
28817         /**
28818          * @event mouseover
28819          * Fires when the mouse is hovering over this menu
28820          * @param {Roo.bootstrap.menu.Item} this
28821          * @param {Roo.EventObject} e
28822          */
28823         mouseover : true,
28824         /**
28825          * @event mouseout
28826          * Fires when the mouse exits this menu
28827          * @param {Roo.bootstrap.menu.Item} this
28828          * @param {Roo.EventObject} e
28829          */
28830         mouseout : true,
28831         // raw events
28832         /**
28833          * @event click
28834          * The raw click event for the entire grid.
28835          * @param {Roo.EventObject} e
28836          */
28837         click : true
28838     });
28839 };
28840
28841 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28842     
28843     submenu : false,
28844     href : '',
28845     html : '',
28846     preventDefault: true,
28847     disable : false,
28848     icon : false,
28849     pos : 'right',
28850     
28851     getAutoCreate : function()
28852     {
28853         var text = [
28854             {
28855                 tag : 'span',
28856                 cls : 'roo-menu-item-text',
28857                 html : this.html
28858             }
28859         ];
28860         
28861         if(this.icon){
28862             text.unshift({
28863                 tag : 'i',
28864                 cls : 'fa ' + this.icon
28865             })
28866         }
28867         
28868         var cfg = {
28869             tag : 'li',
28870             cn : [
28871                 {
28872                     tag : 'a',
28873                     href : this.href || '#',
28874                     cn : text
28875                 }
28876             ]
28877         };
28878         
28879         if(this.disable){
28880             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28881         }
28882         
28883         if(this.submenu){
28884             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28885             
28886             if(this.pos == 'left'){
28887                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28888             }
28889         }
28890         
28891         return cfg;
28892     },
28893     
28894     initEvents : function() 
28895     {
28896         this.el.on('mouseover', this.onMouseOver, this);
28897         this.el.on('mouseout', this.onMouseOut, this);
28898         
28899         this.el.select('a', true).first().on('click', this.onClick, this);
28900         
28901     },
28902     
28903     onClick : function(e)
28904     {
28905         if(this.preventDefault){
28906             e.preventDefault();
28907         }
28908         
28909         this.fireEvent("click", this, e);
28910     },
28911     
28912     onMouseOver : function(e)
28913     {
28914         if(this.submenu && this.pos == 'left'){
28915             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28916         }
28917         
28918         this.fireEvent("mouseover", this, e);
28919     },
28920     
28921     onMouseOut : function(e)
28922     {
28923         this.fireEvent("mouseout", this, e);
28924     }
28925 });
28926
28927  
28928
28929  /*
28930  * - LGPL
28931  *
28932  * menu separator
28933  * 
28934  */
28935 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28936
28937 /**
28938  * @class Roo.bootstrap.menu.Separator
28939  * @extends Roo.bootstrap.Component
28940  * Bootstrap Separator class
28941  * 
28942  * @constructor
28943  * Create a new Separator
28944  * @param {Object} config The config object
28945  */
28946
28947
28948 Roo.bootstrap.menu.Separator = function(config){
28949     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28950 };
28951
28952 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28953     
28954     getAutoCreate : function(){
28955         var cfg = {
28956             tag : 'li',
28957             cls: 'divider'
28958         };
28959         
28960         return cfg;
28961     }
28962    
28963 });
28964
28965  
28966
28967  /*
28968  * - LGPL
28969  *
28970  * Tooltip
28971  * 
28972  */
28973
28974 /**
28975  * @class Roo.bootstrap.Tooltip
28976  * Bootstrap Tooltip class
28977  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28978  * to determine which dom element triggers the tooltip.
28979  * 
28980  * It needs to add support for additional attributes like tooltip-position
28981  * 
28982  * @constructor
28983  * Create a new Toolti
28984  * @param {Object} config The config object
28985  */
28986
28987 Roo.bootstrap.Tooltip = function(config){
28988     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28989     
28990     this.alignment = Roo.bootstrap.Tooltip.alignment;
28991     
28992     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28993         this.alignment = config.alignment;
28994     }
28995     
28996 };
28997
28998 Roo.apply(Roo.bootstrap.Tooltip, {
28999     /**
29000      * @function init initialize tooltip monitoring.
29001      * @static
29002      */
29003     currentEl : false,
29004     currentTip : false,
29005     currentRegion : false,
29006     
29007     //  init : delay?
29008     
29009     init : function()
29010     {
29011         Roo.get(document).on('mouseover', this.enter ,this);
29012         Roo.get(document).on('mouseout', this.leave, this);
29013          
29014         
29015         this.currentTip = new Roo.bootstrap.Tooltip();
29016     },
29017     
29018     enter : function(ev)
29019     {
29020         var dom = ev.getTarget();
29021         
29022         //Roo.log(['enter',dom]);
29023         var el = Roo.fly(dom);
29024         if (this.currentEl) {
29025             //Roo.log(dom);
29026             //Roo.log(this.currentEl);
29027             //Roo.log(this.currentEl.contains(dom));
29028             if (this.currentEl == el) {
29029                 return;
29030             }
29031             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29032                 return;
29033             }
29034
29035         }
29036         
29037         if (this.currentTip.el) {
29038             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29039         }    
29040         //Roo.log(ev);
29041         
29042         if(!el || el.dom == document){
29043             return;
29044         }
29045         
29046         var bindEl = el;
29047         
29048         // you can not look for children, as if el is the body.. then everythign is the child..
29049         if (!el.attr('tooltip')) { //
29050             if (!el.select("[tooltip]").elements.length) {
29051                 return;
29052             }
29053             // is the mouse over this child...?
29054             bindEl = el.select("[tooltip]").first();
29055             var xy = ev.getXY();
29056             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29057                 //Roo.log("not in region.");
29058                 return;
29059             }
29060             //Roo.log("child element over..");
29061             
29062         }
29063         this.currentEl = bindEl;
29064         this.currentTip.bind(bindEl);
29065         this.currentRegion = Roo.lib.Region.getRegion(dom);
29066         this.currentTip.enter();
29067         
29068     },
29069     leave : function(ev)
29070     {
29071         var dom = ev.getTarget();
29072         //Roo.log(['leave',dom]);
29073         if (!this.currentEl) {
29074             return;
29075         }
29076         
29077         
29078         if (dom != this.currentEl.dom) {
29079             return;
29080         }
29081         var xy = ev.getXY();
29082         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29083             return;
29084         }
29085         // only activate leave if mouse cursor is outside... bounding box..
29086         
29087         
29088         
29089         
29090         if (this.currentTip) {
29091             this.currentTip.leave();
29092         }
29093         //Roo.log('clear currentEl');
29094         this.currentEl = false;
29095         
29096         
29097     },
29098     alignment : {
29099         'left' : ['r-l', [-2,0], 'right'],
29100         'right' : ['l-r', [2,0], 'left'],
29101         'bottom' : ['t-b', [0,2], 'top'],
29102         'top' : [ 'b-t', [0,-2], 'bottom']
29103     }
29104     
29105 });
29106
29107
29108 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29109     
29110     
29111     bindEl : false,
29112     
29113     delay : null, // can be { show : 300 , hide: 500}
29114     
29115     timeout : null,
29116     
29117     hoverState : null, //???
29118     
29119     placement : 'bottom', 
29120     
29121     alignment : false,
29122     
29123     getAutoCreate : function(){
29124     
29125         var cfg = {
29126            cls : 'tooltip',   
29127            role : 'tooltip',
29128            cn : [
29129                 {
29130                     cls : 'tooltip-arrow arrow'
29131                 },
29132                 {
29133                     cls : 'tooltip-inner'
29134                 }
29135            ]
29136         };
29137         
29138         return cfg;
29139     },
29140     bind : function(el)
29141     {
29142         this.bindEl = el;
29143     },
29144     
29145     initEvents : function()
29146     {
29147         this.arrowEl = this.el.select('.arrow', true).first();
29148         this.innerEl = this.el.select('.tooltip-inner', true).first();
29149     },
29150     
29151     enter : function () {
29152        
29153         if (this.timeout != null) {
29154             clearTimeout(this.timeout);
29155         }
29156         
29157         this.hoverState = 'in';
29158          //Roo.log("enter - show");
29159         if (!this.delay || !this.delay.show) {
29160             this.show();
29161             return;
29162         }
29163         var _t = this;
29164         this.timeout = setTimeout(function () {
29165             if (_t.hoverState == 'in') {
29166                 _t.show();
29167             }
29168         }, this.delay.show);
29169     },
29170     leave : function()
29171     {
29172         clearTimeout(this.timeout);
29173     
29174         this.hoverState = 'out';
29175          if (!this.delay || !this.delay.hide) {
29176             this.hide();
29177             return;
29178         }
29179        
29180         var _t = this;
29181         this.timeout = setTimeout(function () {
29182             //Roo.log("leave - timeout");
29183             
29184             if (_t.hoverState == 'out') {
29185                 _t.hide();
29186                 Roo.bootstrap.Tooltip.currentEl = false;
29187             }
29188         }, delay);
29189     },
29190     
29191     show : function (msg)
29192     {
29193         if (!this.el) {
29194             this.render(document.body);
29195         }
29196         // set content.
29197         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29198         
29199         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29200         
29201         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29202         
29203         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29204                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29205         
29206         var placement = typeof this.placement == 'function' ?
29207             this.placement.call(this, this.el, on_el) :
29208             this.placement;
29209             
29210         var autoToken = /\s?auto?\s?/i;
29211         var autoPlace = autoToken.test(placement);
29212         if (autoPlace) {
29213             placement = placement.replace(autoToken, '') || 'top';
29214         }
29215         
29216         //this.el.detach()
29217         //this.el.setXY([0,0]);
29218         this.el.show();
29219         //this.el.dom.style.display='block';
29220         
29221         //this.el.appendTo(on_el);
29222         
29223         var p = this.getPosition();
29224         var box = this.el.getBox();
29225         
29226         if (autoPlace) {
29227             // fixme..
29228         }
29229         
29230         var align = this.alignment[placement];
29231         
29232         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29233         
29234         if(placement == 'top' || placement == 'bottom'){
29235             if(xy[0] < 0){
29236                 placement = 'right';
29237             }
29238             
29239             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29240                 placement = 'left';
29241             }
29242             
29243             var scroll = Roo.select('body', true).first().getScroll();
29244             
29245             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29246                 placement = 'top';
29247             }
29248             
29249             align = this.alignment[placement];
29250             
29251             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29252             
29253         }
29254         
29255         this.el.alignTo(this.bindEl, align[0],align[1]);
29256         //var arrow = this.el.select('.arrow',true).first();
29257         //arrow.set(align[2], 
29258         
29259         this.el.addClass(placement);
29260         this.el.addClass("bs-tooltip-"+ placement);
29261         
29262         this.el.addClass('in fade show');
29263         
29264         this.hoverState = null;
29265         
29266         if (this.el.hasClass('fade')) {
29267             // fade it?
29268         }
29269         
29270         
29271         
29272         
29273         
29274     },
29275     hide : function()
29276     {
29277          
29278         if (!this.el) {
29279             return;
29280         }
29281         //this.el.setXY([0,0]);
29282         this.el.removeClass(['show', 'in']);
29283         //this.el.hide();
29284         
29285     }
29286     
29287 });
29288  
29289
29290  /*
29291  * - LGPL
29292  *
29293  * Location Picker
29294  * 
29295  */
29296
29297 /**
29298  * @class Roo.bootstrap.LocationPicker
29299  * @extends Roo.bootstrap.Component
29300  * Bootstrap LocationPicker class
29301  * @cfg {Number} latitude Position when init default 0
29302  * @cfg {Number} longitude Position when init default 0
29303  * @cfg {Number} zoom default 15
29304  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29305  * @cfg {Boolean} mapTypeControl default false
29306  * @cfg {Boolean} disableDoubleClickZoom default false
29307  * @cfg {Boolean} scrollwheel default true
29308  * @cfg {Boolean} streetViewControl default false
29309  * @cfg {Number} radius default 0
29310  * @cfg {String} locationName
29311  * @cfg {Boolean} draggable default true
29312  * @cfg {Boolean} enableAutocomplete default false
29313  * @cfg {Boolean} enableReverseGeocode default true
29314  * @cfg {String} markerTitle
29315  * 
29316  * @constructor
29317  * Create a new LocationPicker
29318  * @param {Object} config The config object
29319  */
29320
29321
29322 Roo.bootstrap.LocationPicker = function(config){
29323     
29324     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29325     
29326     this.addEvents({
29327         /**
29328          * @event initial
29329          * Fires when the picker initialized.
29330          * @param {Roo.bootstrap.LocationPicker} this
29331          * @param {Google Location} location
29332          */
29333         initial : true,
29334         /**
29335          * @event positionchanged
29336          * Fires when the picker position changed.
29337          * @param {Roo.bootstrap.LocationPicker} this
29338          * @param {Google Location} location
29339          */
29340         positionchanged : true,
29341         /**
29342          * @event resize
29343          * Fires when the map resize.
29344          * @param {Roo.bootstrap.LocationPicker} this
29345          */
29346         resize : true,
29347         /**
29348          * @event show
29349          * Fires when the map show.
29350          * @param {Roo.bootstrap.LocationPicker} this
29351          */
29352         show : true,
29353         /**
29354          * @event hide
29355          * Fires when the map hide.
29356          * @param {Roo.bootstrap.LocationPicker} this
29357          */
29358         hide : true,
29359         /**
29360          * @event mapClick
29361          * Fires when click the map.
29362          * @param {Roo.bootstrap.LocationPicker} this
29363          * @param {Map event} e
29364          */
29365         mapClick : true,
29366         /**
29367          * @event mapRightClick
29368          * Fires when right click the map.
29369          * @param {Roo.bootstrap.LocationPicker} this
29370          * @param {Map event} e
29371          */
29372         mapRightClick : true,
29373         /**
29374          * @event markerClick
29375          * Fires when click the marker.
29376          * @param {Roo.bootstrap.LocationPicker} this
29377          * @param {Map event} e
29378          */
29379         markerClick : true,
29380         /**
29381          * @event markerRightClick
29382          * Fires when right click the marker.
29383          * @param {Roo.bootstrap.LocationPicker} this
29384          * @param {Map event} e
29385          */
29386         markerRightClick : true,
29387         /**
29388          * @event OverlayViewDraw
29389          * Fires when OverlayView Draw
29390          * @param {Roo.bootstrap.LocationPicker} this
29391          */
29392         OverlayViewDraw : true,
29393         /**
29394          * @event OverlayViewOnAdd
29395          * Fires when OverlayView Draw
29396          * @param {Roo.bootstrap.LocationPicker} this
29397          */
29398         OverlayViewOnAdd : true,
29399         /**
29400          * @event OverlayViewOnRemove
29401          * Fires when OverlayView Draw
29402          * @param {Roo.bootstrap.LocationPicker} this
29403          */
29404         OverlayViewOnRemove : true,
29405         /**
29406          * @event OverlayViewShow
29407          * Fires when OverlayView Draw
29408          * @param {Roo.bootstrap.LocationPicker} this
29409          * @param {Pixel} cpx
29410          */
29411         OverlayViewShow : true,
29412         /**
29413          * @event OverlayViewHide
29414          * Fires when OverlayView Draw
29415          * @param {Roo.bootstrap.LocationPicker} this
29416          */
29417         OverlayViewHide : true,
29418         /**
29419          * @event loadexception
29420          * Fires when load google lib failed.
29421          * @param {Roo.bootstrap.LocationPicker} this
29422          */
29423         loadexception : true
29424     });
29425         
29426 };
29427
29428 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29429     
29430     gMapContext: false,
29431     
29432     latitude: 0,
29433     longitude: 0,
29434     zoom: 15,
29435     mapTypeId: false,
29436     mapTypeControl: false,
29437     disableDoubleClickZoom: false,
29438     scrollwheel: true,
29439     streetViewControl: false,
29440     radius: 0,
29441     locationName: '',
29442     draggable: true,
29443     enableAutocomplete: false,
29444     enableReverseGeocode: true,
29445     markerTitle: '',
29446     
29447     getAutoCreate: function()
29448     {
29449
29450         var cfg = {
29451             tag: 'div',
29452             cls: 'roo-location-picker'
29453         };
29454         
29455         return cfg
29456     },
29457     
29458     initEvents: function(ct, position)
29459     {       
29460         if(!this.el.getWidth() || this.isApplied()){
29461             return;
29462         }
29463         
29464         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29465         
29466         this.initial();
29467     },
29468     
29469     initial: function()
29470     {
29471         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29472             this.fireEvent('loadexception', this);
29473             return;
29474         }
29475         
29476         if(!this.mapTypeId){
29477             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29478         }
29479         
29480         this.gMapContext = this.GMapContext();
29481         
29482         this.initOverlayView();
29483         
29484         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29485         
29486         var _this = this;
29487                 
29488         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29489             _this.setPosition(_this.gMapContext.marker.position);
29490         });
29491         
29492         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29493             _this.fireEvent('mapClick', this, event);
29494             
29495         });
29496
29497         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29498             _this.fireEvent('mapRightClick', this, event);
29499             
29500         });
29501         
29502         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29503             _this.fireEvent('markerClick', this, event);
29504             
29505         });
29506
29507         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29508             _this.fireEvent('markerRightClick', this, event);
29509             
29510         });
29511         
29512         this.setPosition(this.gMapContext.location);
29513         
29514         this.fireEvent('initial', this, this.gMapContext.location);
29515     },
29516     
29517     initOverlayView: function()
29518     {
29519         var _this = this;
29520         
29521         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29522             
29523             draw: function()
29524             {
29525                 _this.fireEvent('OverlayViewDraw', _this);
29526             },
29527             
29528             onAdd: function()
29529             {
29530                 _this.fireEvent('OverlayViewOnAdd', _this);
29531             },
29532             
29533             onRemove: function()
29534             {
29535                 _this.fireEvent('OverlayViewOnRemove', _this);
29536             },
29537             
29538             show: function(cpx)
29539             {
29540                 _this.fireEvent('OverlayViewShow', _this, cpx);
29541             },
29542             
29543             hide: function()
29544             {
29545                 _this.fireEvent('OverlayViewHide', _this);
29546             }
29547             
29548         });
29549     },
29550     
29551     fromLatLngToContainerPixel: function(event)
29552     {
29553         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29554     },
29555     
29556     isApplied: function() 
29557     {
29558         return this.getGmapContext() == false ? false : true;
29559     },
29560     
29561     getGmapContext: function() 
29562     {
29563         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29564     },
29565     
29566     GMapContext: function() 
29567     {
29568         var position = new google.maps.LatLng(this.latitude, this.longitude);
29569         
29570         var _map = new google.maps.Map(this.el.dom, {
29571             center: position,
29572             zoom: this.zoom,
29573             mapTypeId: this.mapTypeId,
29574             mapTypeControl: this.mapTypeControl,
29575             disableDoubleClickZoom: this.disableDoubleClickZoom,
29576             scrollwheel: this.scrollwheel,
29577             streetViewControl: this.streetViewControl,
29578             locationName: this.locationName,
29579             draggable: this.draggable,
29580             enableAutocomplete: this.enableAutocomplete,
29581             enableReverseGeocode: this.enableReverseGeocode
29582         });
29583         
29584         var _marker = new google.maps.Marker({
29585             position: position,
29586             map: _map,
29587             title: this.markerTitle,
29588             draggable: this.draggable
29589         });
29590         
29591         return {
29592             map: _map,
29593             marker: _marker,
29594             circle: null,
29595             location: position,
29596             radius: this.radius,
29597             locationName: this.locationName,
29598             addressComponents: {
29599                 formatted_address: null,
29600                 addressLine1: null,
29601                 addressLine2: null,
29602                 streetName: null,
29603                 streetNumber: null,
29604                 city: null,
29605                 district: null,
29606                 state: null,
29607                 stateOrProvince: null
29608             },
29609             settings: this,
29610             domContainer: this.el.dom,
29611             geodecoder: new google.maps.Geocoder()
29612         };
29613     },
29614     
29615     drawCircle: function(center, radius, options) 
29616     {
29617         if (this.gMapContext.circle != null) {
29618             this.gMapContext.circle.setMap(null);
29619         }
29620         if (radius > 0) {
29621             radius *= 1;
29622             options = Roo.apply({}, options, {
29623                 strokeColor: "#0000FF",
29624                 strokeOpacity: .35,
29625                 strokeWeight: 2,
29626                 fillColor: "#0000FF",
29627                 fillOpacity: .2
29628             });
29629             
29630             options.map = this.gMapContext.map;
29631             options.radius = radius;
29632             options.center = center;
29633             this.gMapContext.circle = new google.maps.Circle(options);
29634             return this.gMapContext.circle;
29635         }
29636         
29637         return null;
29638     },
29639     
29640     setPosition: function(location) 
29641     {
29642         this.gMapContext.location = location;
29643         this.gMapContext.marker.setPosition(location);
29644         this.gMapContext.map.panTo(location);
29645         this.drawCircle(location, this.gMapContext.radius, {});
29646         
29647         var _this = this;
29648         
29649         if (this.gMapContext.settings.enableReverseGeocode) {
29650             this.gMapContext.geodecoder.geocode({
29651                 latLng: this.gMapContext.location
29652             }, function(results, status) {
29653                 
29654                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29655                     _this.gMapContext.locationName = results[0].formatted_address;
29656                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29657                     
29658                     _this.fireEvent('positionchanged', this, location);
29659                 }
29660             });
29661             
29662             return;
29663         }
29664         
29665         this.fireEvent('positionchanged', this, location);
29666     },
29667     
29668     resize: function()
29669     {
29670         google.maps.event.trigger(this.gMapContext.map, "resize");
29671         
29672         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29673         
29674         this.fireEvent('resize', this);
29675     },
29676     
29677     setPositionByLatLng: function(latitude, longitude)
29678     {
29679         this.setPosition(new google.maps.LatLng(latitude, longitude));
29680     },
29681     
29682     getCurrentPosition: function() 
29683     {
29684         return {
29685             latitude: this.gMapContext.location.lat(),
29686             longitude: this.gMapContext.location.lng()
29687         };
29688     },
29689     
29690     getAddressName: function() 
29691     {
29692         return this.gMapContext.locationName;
29693     },
29694     
29695     getAddressComponents: function() 
29696     {
29697         return this.gMapContext.addressComponents;
29698     },
29699     
29700     address_component_from_google_geocode: function(address_components) 
29701     {
29702         var result = {};
29703         
29704         for (var i = 0; i < address_components.length; i++) {
29705             var component = address_components[i];
29706             if (component.types.indexOf("postal_code") >= 0) {
29707                 result.postalCode = component.short_name;
29708             } else if (component.types.indexOf("street_number") >= 0) {
29709                 result.streetNumber = component.short_name;
29710             } else if (component.types.indexOf("route") >= 0) {
29711                 result.streetName = component.short_name;
29712             } else if (component.types.indexOf("neighborhood") >= 0) {
29713                 result.city = component.short_name;
29714             } else if (component.types.indexOf("locality") >= 0) {
29715                 result.city = component.short_name;
29716             } else if (component.types.indexOf("sublocality") >= 0) {
29717                 result.district = component.short_name;
29718             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29719                 result.stateOrProvince = component.short_name;
29720             } else if (component.types.indexOf("country") >= 0) {
29721                 result.country = component.short_name;
29722             }
29723         }
29724         
29725         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29726         result.addressLine2 = "";
29727         return result;
29728     },
29729     
29730     setZoomLevel: function(zoom)
29731     {
29732         this.gMapContext.map.setZoom(zoom);
29733     },
29734     
29735     show: function()
29736     {
29737         if(!this.el){
29738             return;
29739         }
29740         
29741         this.el.show();
29742         
29743         this.resize();
29744         
29745         this.fireEvent('show', this);
29746     },
29747     
29748     hide: function()
29749     {
29750         if(!this.el){
29751             return;
29752         }
29753         
29754         this.el.hide();
29755         
29756         this.fireEvent('hide', this);
29757     }
29758     
29759 });
29760
29761 Roo.apply(Roo.bootstrap.LocationPicker, {
29762     
29763     OverlayView : function(map, options)
29764     {
29765         options = options || {};
29766         
29767         this.setMap(map);
29768     }
29769     
29770     
29771 });/**
29772  * @class Roo.bootstrap.Alert
29773  * @extends Roo.bootstrap.Component
29774  * Bootstrap Alert class - shows an alert area box
29775  * eg
29776  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29777   Enter a valid email address
29778 </div>
29779  * @licence LGPL
29780  * @cfg {String} title The title of alert
29781  * @cfg {String} html The content of alert
29782  * @cfg {String} weight (  success | info | warning | danger )
29783  * @cfg {String} faicon font-awesomeicon
29784  * 
29785  * @constructor
29786  * Create a new alert
29787  * @param {Object} config The config object
29788  */
29789
29790
29791 Roo.bootstrap.Alert = function(config){
29792     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29793     
29794 };
29795
29796 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29797     
29798     title: '',
29799     html: '',
29800     weight: false,
29801     faicon: false,
29802     
29803     getAutoCreate : function()
29804     {
29805         
29806         var cfg = {
29807             tag : 'div',
29808             cls : 'alert',
29809             cn : [
29810                 {
29811                     tag : 'i',
29812                     cls : 'roo-alert-icon'
29813                     
29814                 },
29815                 {
29816                     tag : 'b',
29817                     cls : 'roo-alert-title',
29818                     html : this.title
29819                 },
29820                 {
29821                     tag : 'span',
29822                     cls : 'roo-alert-text',
29823                     html : this.html
29824                 }
29825             ]
29826         };
29827         
29828         if(this.faicon){
29829             cfg.cn[0].cls += ' fa ' + this.faicon;
29830         }
29831         
29832         if(this.weight){
29833             cfg.cls += ' alert-' + this.weight;
29834         }
29835         
29836         return cfg;
29837     },
29838     
29839     initEvents: function() 
29840     {
29841         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29842     },
29843     
29844     setTitle : function(str)
29845     {
29846         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29847     },
29848     
29849     setText : function(str)
29850     {
29851         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29852     },
29853     
29854     setWeight : function(weight)
29855     {
29856         if(this.weight){
29857             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29858         }
29859         
29860         this.weight = weight;
29861         
29862         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29863     },
29864     
29865     setIcon : function(icon)
29866     {
29867         if(this.faicon){
29868             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29869         }
29870         
29871         this.faicon = icon;
29872         
29873         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29874     },
29875     
29876     hide: function() 
29877     {
29878         this.el.hide();   
29879     },
29880     
29881     show: function() 
29882     {  
29883         this.el.show();   
29884     }
29885     
29886 });
29887
29888  
29889 /*
29890 * Licence: LGPL
29891 */
29892
29893 /**
29894  * @class Roo.bootstrap.UploadCropbox
29895  * @extends Roo.bootstrap.Component
29896  * Bootstrap UploadCropbox class
29897  * @cfg {String} emptyText show when image has been loaded
29898  * @cfg {String} rotateNotify show when image too small to rotate
29899  * @cfg {Number} errorTimeout default 3000
29900  * @cfg {Number} minWidth default 300
29901  * @cfg {Number} minHeight default 300
29902  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29903  * @cfg {Boolean} isDocument (true|false) default false
29904  * @cfg {String} url action url
29905  * @cfg {String} paramName default 'imageUpload'
29906  * @cfg {String} method default POST
29907  * @cfg {Boolean} loadMask (true|false) default true
29908  * @cfg {Boolean} loadingText default 'Loading...'
29909  * 
29910  * @constructor
29911  * Create a new UploadCropbox
29912  * @param {Object} config The config object
29913  */
29914
29915 Roo.bootstrap.UploadCropbox = function(config){
29916     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29917     
29918     this.addEvents({
29919         /**
29920          * @event beforeselectfile
29921          * Fire before select file
29922          * @param {Roo.bootstrap.UploadCropbox} this
29923          */
29924         "beforeselectfile" : true,
29925         /**
29926          * @event initial
29927          * Fire after initEvent
29928          * @param {Roo.bootstrap.UploadCropbox} this
29929          */
29930         "initial" : true,
29931         /**
29932          * @event crop
29933          * Fire after initEvent
29934          * @param {Roo.bootstrap.UploadCropbox} this
29935          * @param {String} data
29936          */
29937         "crop" : true,
29938         /**
29939          * @event prepare
29940          * Fire when preparing the file data
29941          * @param {Roo.bootstrap.UploadCropbox} this
29942          * @param {Object} file
29943          */
29944         "prepare" : true,
29945         /**
29946          * @event exception
29947          * Fire when get exception
29948          * @param {Roo.bootstrap.UploadCropbox} this
29949          * @param {XMLHttpRequest} xhr
29950          */
29951         "exception" : true,
29952         /**
29953          * @event beforeloadcanvas
29954          * Fire before load the canvas
29955          * @param {Roo.bootstrap.UploadCropbox} this
29956          * @param {String} src
29957          */
29958         "beforeloadcanvas" : true,
29959         /**
29960          * @event trash
29961          * Fire when trash image
29962          * @param {Roo.bootstrap.UploadCropbox} this
29963          */
29964         "trash" : true,
29965         /**
29966          * @event download
29967          * Fire when download the image
29968          * @param {Roo.bootstrap.UploadCropbox} this
29969          */
29970         "download" : true,
29971         /**
29972          * @event footerbuttonclick
29973          * Fire when footerbuttonclick
29974          * @param {Roo.bootstrap.UploadCropbox} this
29975          * @param {String} type
29976          */
29977         "footerbuttonclick" : true,
29978         /**
29979          * @event resize
29980          * Fire when resize
29981          * @param {Roo.bootstrap.UploadCropbox} this
29982          */
29983         "resize" : true,
29984         /**
29985          * @event rotate
29986          * Fire when rotate the image
29987          * @param {Roo.bootstrap.UploadCropbox} this
29988          * @param {String} pos
29989          */
29990         "rotate" : true,
29991         /**
29992          * @event inspect
29993          * Fire when inspect the file
29994          * @param {Roo.bootstrap.UploadCropbox} this
29995          * @param {Object} file
29996          */
29997         "inspect" : true,
29998         /**
29999          * @event upload
30000          * Fire when xhr upload the file
30001          * @param {Roo.bootstrap.UploadCropbox} this
30002          * @param {Object} data
30003          */
30004         "upload" : true,
30005         /**
30006          * @event arrange
30007          * Fire when arrange the file data
30008          * @param {Roo.bootstrap.UploadCropbox} this
30009          * @param {Object} formData
30010          */
30011         "arrange" : true
30012     });
30013     
30014     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30015 };
30016
30017 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30018     
30019     emptyText : 'Click to upload image',
30020     rotateNotify : 'Image is too small to rotate',
30021     errorTimeout : 3000,
30022     scale : 0,
30023     baseScale : 1,
30024     rotate : 0,
30025     dragable : false,
30026     pinching : false,
30027     mouseX : 0,
30028     mouseY : 0,
30029     cropData : false,
30030     minWidth : 300,
30031     minHeight : 300,
30032     file : false,
30033     exif : {},
30034     baseRotate : 1,
30035     cropType : 'image/jpeg',
30036     buttons : false,
30037     canvasLoaded : false,
30038     isDocument : false,
30039     method : 'POST',
30040     paramName : 'imageUpload',
30041     loadMask : true,
30042     loadingText : 'Loading...',
30043     maskEl : false,
30044     
30045     getAutoCreate : function()
30046     {
30047         var cfg = {
30048             tag : 'div',
30049             cls : 'roo-upload-cropbox',
30050             cn : [
30051                 {
30052                     tag : 'input',
30053                     cls : 'roo-upload-cropbox-selector',
30054                     type : 'file'
30055                 },
30056                 {
30057                     tag : 'div',
30058                     cls : 'roo-upload-cropbox-body',
30059                     style : 'cursor:pointer',
30060                     cn : [
30061                         {
30062                             tag : 'div',
30063                             cls : 'roo-upload-cropbox-preview'
30064                         },
30065                         {
30066                             tag : 'div',
30067                             cls : 'roo-upload-cropbox-thumb'
30068                         },
30069                         {
30070                             tag : 'div',
30071                             cls : 'roo-upload-cropbox-empty-notify',
30072                             html : this.emptyText
30073                         },
30074                         {
30075                             tag : 'div',
30076                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30077                             html : this.rotateNotify
30078                         }
30079                     ]
30080                 },
30081                 {
30082                     tag : 'div',
30083                     cls : 'roo-upload-cropbox-footer',
30084                     cn : {
30085                         tag : 'div',
30086                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30087                         cn : []
30088                     }
30089                 }
30090             ]
30091         };
30092         
30093         return cfg;
30094     },
30095     
30096     onRender : function(ct, position)
30097     {
30098         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30099         
30100         if (this.buttons.length) {
30101             
30102             Roo.each(this.buttons, function(bb) {
30103                 
30104                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30105                 
30106                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30107                 
30108             }, this);
30109         }
30110         
30111         if(this.loadMask){
30112             this.maskEl = this.el;
30113         }
30114     },
30115     
30116     initEvents : function()
30117     {
30118         this.urlAPI = (window.createObjectURL && window) || 
30119                                 (window.URL && URL.revokeObjectURL && URL) || 
30120                                 (window.webkitURL && webkitURL);
30121                         
30122         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30123         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30124         
30125         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30126         this.selectorEl.hide();
30127         
30128         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30129         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30130         
30131         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30132         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30133         this.thumbEl.hide();
30134         
30135         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30136         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30137         
30138         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30139         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30140         this.errorEl.hide();
30141         
30142         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30143         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30144         this.footerEl.hide();
30145         
30146         this.setThumbBoxSize();
30147         
30148         this.bind();
30149         
30150         this.resize();
30151         
30152         this.fireEvent('initial', this);
30153     },
30154
30155     bind : function()
30156     {
30157         var _this = this;
30158         
30159         window.addEventListener("resize", function() { _this.resize(); } );
30160         
30161         this.bodyEl.on('click', this.beforeSelectFile, this);
30162         
30163         if(Roo.isTouch){
30164             this.bodyEl.on('touchstart', this.onTouchStart, this);
30165             this.bodyEl.on('touchmove', this.onTouchMove, this);
30166             this.bodyEl.on('touchend', this.onTouchEnd, this);
30167         }
30168         
30169         if(!Roo.isTouch){
30170             this.bodyEl.on('mousedown', this.onMouseDown, this);
30171             this.bodyEl.on('mousemove', this.onMouseMove, this);
30172             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30173             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30174             Roo.get(document).on('mouseup', this.onMouseUp, this);
30175         }
30176         
30177         this.selectorEl.on('change', this.onFileSelected, this);
30178     },
30179     
30180     reset : function()
30181     {    
30182         this.scale = 0;
30183         this.baseScale = 1;
30184         this.rotate = 0;
30185         this.baseRotate = 1;
30186         this.dragable = false;
30187         this.pinching = false;
30188         this.mouseX = 0;
30189         this.mouseY = 0;
30190         this.cropData = false;
30191         this.notifyEl.dom.innerHTML = this.emptyText;
30192         
30193         this.selectorEl.dom.value = '';
30194         
30195     },
30196     
30197     resize : function()
30198     {
30199         if(this.fireEvent('resize', this) != false){
30200             this.setThumbBoxPosition();
30201             this.setCanvasPosition();
30202         }
30203     },
30204     
30205     onFooterButtonClick : function(e, el, o, type)
30206     {
30207         switch (type) {
30208             case 'rotate-left' :
30209                 this.onRotateLeft(e);
30210                 break;
30211             case 'rotate-right' :
30212                 this.onRotateRight(e);
30213                 break;
30214             case 'picture' :
30215                 this.beforeSelectFile(e);
30216                 break;
30217             case 'trash' :
30218                 this.trash(e);
30219                 break;
30220             case 'crop' :
30221                 this.crop(e);
30222                 break;
30223             case 'download' :
30224                 this.download(e);
30225                 break;
30226             default :
30227                 break;
30228         }
30229         
30230         this.fireEvent('footerbuttonclick', this, type);
30231     },
30232     
30233     beforeSelectFile : function(e)
30234     {
30235         e.preventDefault();
30236         
30237         if(this.fireEvent('beforeselectfile', this) != false){
30238             this.selectorEl.dom.click();
30239         }
30240     },
30241     
30242     onFileSelected : function(e)
30243     {
30244         e.preventDefault();
30245         
30246         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30247             return;
30248         }
30249         
30250         var file = this.selectorEl.dom.files[0];
30251         
30252         if(this.fireEvent('inspect', this, file) != false){
30253             this.prepare(file);
30254         }
30255         
30256     },
30257     
30258     trash : function(e)
30259     {
30260         this.fireEvent('trash', this);
30261     },
30262     
30263     download : function(e)
30264     {
30265         this.fireEvent('download', this);
30266     },
30267     
30268     loadCanvas : function(src)
30269     {   
30270         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30271             
30272             this.reset();
30273             
30274             this.imageEl = document.createElement('img');
30275             
30276             var _this = this;
30277             
30278             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30279             
30280             this.imageEl.src = src;
30281         }
30282     },
30283     
30284     onLoadCanvas : function()
30285     {   
30286         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30287         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30288         
30289         this.bodyEl.un('click', this.beforeSelectFile, this);
30290         
30291         this.notifyEl.hide();
30292         this.thumbEl.show();
30293         this.footerEl.show();
30294         
30295         this.baseRotateLevel();
30296         
30297         if(this.isDocument){
30298             this.setThumbBoxSize();
30299         }
30300         
30301         this.setThumbBoxPosition();
30302         
30303         this.baseScaleLevel();
30304         
30305         this.draw();
30306         
30307         this.resize();
30308         
30309         this.canvasLoaded = true;
30310         
30311         if(this.loadMask){
30312             this.maskEl.unmask();
30313         }
30314         
30315     },
30316     
30317     setCanvasPosition : function()
30318     {   
30319         if(!this.canvasEl){
30320             return;
30321         }
30322         
30323         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30324         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30325         
30326         this.previewEl.setLeft(pw);
30327         this.previewEl.setTop(ph);
30328         
30329     },
30330     
30331     onMouseDown : function(e)
30332     {   
30333         e.stopEvent();
30334         
30335         this.dragable = true;
30336         this.pinching = false;
30337         
30338         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30339             this.dragable = false;
30340             return;
30341         }
30342         
30343         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30344         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30345         
30346     },
30347     
30348     onMouseMove : function(e)
30349     {   
30350         e.stopEvent();
30351         
30352         if(!this.canvasLoaded){
30353             return;
30354         }
30355         
30356         if (!this.dragable){
30357             return;
30358         }
30359         
30360         var minX = Math.ceil(this.thumbEl.getLeft(true));
30361         var minY = Math.ceil(this.thumbEl.getTop(true));
30362         
30363         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30364         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30365         
30366         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30367         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30368         
30369         x = x - this.mouseX;
30370         y = y - this.mouseY;
30371         
30372         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30373         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30374         
30375         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30376         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30377         
30378         this.previewEl.setLeft(bgX);
30379         this.previewEl.setTop(bgY);
30380         
30381         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30382         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30383     },
30384     
30385     onMouseUp : function(e)
30386     {   
30387         e.stopEvent();
30388         
30389         this.dragable = false;
30390     },
30391     
30392     onMouseWheel : function(e)
30393     {   
30394         e.stopEvent();
30395         
30396         this.startScale = this.scale;
30397         
30398         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30399         
30400         if(!this.zoomable()){
30401             this.scale = this.startScale;
30402             return;
30403         }
30404         
30405         this.draw();
30406         
30407         return;
30408     },
30409     
30410     zoomable : function()
30411     {
30412         var minScale = this.thumbEl.getWidth() / this.minWidth;
30413         
30414         if(this.minWidth < this.minHeight){
30415             minScale = this.thumbEl.getHeight() / this.minHeight;
30416         }
30417         
30418         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30419         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30420         
30421         if(
30422                 this.isDocument &&
30423                 (this.rotate == 0 || this.rotate == 180) && 
30424                 (
30425                     width > this.imageEl.OriginWidth || 
30426                     height > this.imageEl.OriginHeight ||
30427                     (width < this.minWidth && height < this.minHeight)
30428                 )
30429         ){
30430             return false;
30431         }
30432         
30433         if(
30434                 this.isDocument &&
30435                 (this.rotate == 90 || this.rotate == 270) && 
30436                 (
30437                     width > this.imageEl.OriginWidth || 
30438                     height > this.imageEl.OriginHeight ||
30439                     (width < this.minHeight && height < this.minWidth)
30440                 )
30441         ){
30442             return false;
30443         }
30444         
30445         if(
30446                 !this.isDocument &&
30447                 (this.rotate == 0 || this.rotate == 180) && 
30448                 (
30449                     width < this.minWidth || 
30450                     width > this.imageEl.OriginWidth || 
30451                     height < this.minHeight || 
30452                     height > this.imageEl.OriginHeight
30453                 )
30454         ){
30455             return false;
30456         }
30457         
30458         if(
30459                 !this.isDocument &&
30460                 (this.rotate == 90 || this.rotate == 270) && 
30461                 (
30462                     width < this.minHeight || 
30463                     width > this.imageEl.OriginWidth || 
30464                     height < this.minWidth || 
30465                     height > this.imageEl.OriginHeight
30466                 )
30467         ){
30468             return false;
30469         }
30470         
30471         return true;
30472         
30473     },
30474     
30475     onRotateLeft : function(e)
30476     {   
30477         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30478             
30479             var minScale = this.thumbEl.getWidth() / this.minWidth;
30480             
30481             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30482             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30483             
30484             this.startScale = this.scale;
30485             
30486             while (this.getScaleLevel() < minScale){
30487             
30488                 this.scale = this.scale + 1;
30489                 
30490                 if(!this.zoomable()){
30491                     break;
30492                 }
30493                 
30494                 if(
30495                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30496                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30497                 ){
30498                     continue;
30499                 }
30500                 
30501                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30502
30503                 this.draw();
30504                 
30505                 return;
30506             }
30507             
30508             this.scale = this.startScale;
30509             
30510             this.onRotateFail();
30511             
30512             return false;
30513         }
30514         
30515         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30516
30517         if(this.isDocument){
30518             this.setThumbBoxSize();
30519             this.setThumbBoxPosition();
30520             this.setCanvasPosition();
30521         }
30522         
30523         this.draw();
30524         
30525         this.fireEvent('rotate', this, 'left');
30526         
30527     },
30528     
30529     onRotateRight : function(e)
30530     {
30531         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30532             
30533             var minScale = this.thumbEl.getWidth() / this.minWidth;
30534         
30535             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30536             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30537             
30538             this.startScale = this.scale;
30539             
30540             while (this.getScaleLevel() < minScale){
30541             
30542                 this.scale = this.scale + 1;
30543                 
30544                 if(!this.zoomable()){
30545                     break;
30546                 }
30547                 
30548                 if(
30549                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30550                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30551                 ){
30552                     continue;
30553                 }
30554                 
30555                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30556
30557                 this.draw();
30558                 
30559                 return;
30560             }
30561             
30562             this.scale = this.startScale;
30563             
30564             this.onRotateFail();
30565             
30566             return false;
30567         }
30568         
30569         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30570
30571         if(this.isDocument){
30572             this.setThumbBoxSize();
30573             this.setThumbBoxPosition();
30574             this.setCanvasPosition();
30575         }
30576         
30577         this.draw();
30578         
30579         this.fireEvent('rotate', this, 'right');
30580     },
30581     
30582     onRotateFail : function()
30583     {
30584         this.errorEl.show(true);
30585         
30586         var _this = this;
30587         
30588         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30589     },
30590     
30591     draw : function()
30592     {
30593         this.previewEl.dom.innerHTML = '';
30594         
30595         var canvasEl = document.createElement("canvas");
30596         
30597         var contextEl = canvasEl.getContext("2d");
30598         
30599         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30600         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30601         var center = this.imageEl.OriginWidth / 2;
30602         
30603         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30604             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30605             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30606             center = this.imageEl.OriginHeight / 2;
30607         }
30608         
30609         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30610         
30611         contextEl.translate(center, center);
30612         contextEl.rotate(this.rotate * Math.PI / 180);
30613
30614         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30615         
30616         this.canvasEl = document.createElement("canvas");
30617         
30618         this.contextEl = this.canvasEl.getContext("2d");
30619         
30620         switch (this.rotate) {
30621             case 0 :
30622                 
30623                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30624                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30625                 
30626                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30627                 
30628                 break;
30629             case 90 : 
30630                 
30631                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30632                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30633                 
30634                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30635                     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);
30636                     break;
30637                 }
30638                 
30639                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30640                 
30641                 break;
30642             case 180 :
30643                 
30644                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30645                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30646                 
30647                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30648                     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);
30649                     break;
30650                 }
30651                 
30652                 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);
30653                 
30654                 break;
30655             case 270 :
30656                 
30657                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30658                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30659         
30660                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30661                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30662                     break;
30663                 }
30664                 
30665                 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);
30666                 
30667                 break;
30668             default : 
30669                 break;
30670         }
30671         
30672         this.previewEl.appendChild(this.canvasEl);
30673         
30674         this.setCanvasPosition();
30675     },
30676     
30677     crop : function()
30678     {
30679         if(!this.canvasLoaded){
30680             return;
30681         }
30682         
30683         var imageCanvas = document.createElement("canvas");
30684         
30685         var imageContext = imageCanvas.getContext("2d");
30686         
30687         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30688         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30689         
30690         var center = imageCanvas.width / 2;
30691         
30692         imageContext.translate(center, center);
30693         
30694         imageContext.rotate(this.rotate * Math.PI / 180);
30695         
30696         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30697         
30698         var canvas = document.createElement("canvas");
30699         
30700         var context = canvas.getContext("2d");
30701                 
30702         canvas.width = this.minWidth;
30703         canvas.height = this.minHeight;
30704
30705         switch (this.rotate) {
30706             case 0 :
30707                 
30708                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30709                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30710                 
30711                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30712                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30713                 
30714                 var targetWidth = this.minWidth - 2 * x;
30715                 var targetHeight = this.minHeight - 2 * y;
30716                 
30717                 var scale = 1;
30718                 
30719                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30720                     scale = targetWidth / width;
30721                 }
30722                 
30723                 if(x > 0 && y == 0){
30724                     scale = targetHeight / height;
30725                 }
30726                 
30727                 if(x > 0 && y > 0){
30728                     scale = targetWidth / width;
30729                     
30730                     if(width < height){
30731                         scale = targetHeight / height;
30732                     }
30733                 }
30734                 
30735                 context.scale(scale, scale);
30736                 
30737                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30738                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30739
30740                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30741                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30742
30743                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30744                 
30745                 break;
30746             case 90 : 
30747                 
30748                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30749                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30750                 
30751                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30752                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30753                 
30754                 var targetWidth = this.minWidth - 2 * x;
30755                 var targetHeight = this.minHeight - 2 * y;
30756                 
30757                 var scale = 1;
30758                 
30759                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30760                     scale = targetWidth / width;
30761                 }
30762                 
30763                 if(x > 0 && y == 0){
30764                     scale = targetHeight / height;
30765                 }
30766                 
30767                 if(x > 0 && y > 0){
30768                     scale = targetWidth / width;
30769                     
30770                     if(width < height){
30771                         scale = targetHeight / height;
30772                     }
30773                 }
30774                 
30775                 context.scale(scale, scale);
30776                 
30777                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30778                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30779
30780                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30781                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30782                 
30783                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30784                 
30785                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30786                 
30787                 break;
30788             case 180 :
30789                 
30790                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30791                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30792                 
30793                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30794                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30795                 
30796                 var targetWidth = this.minWidth - 2 * x;
30797                 var targetHeight = this.minHeight - 2 * y;
30798                 
30799                 var scale = 1;
30800                 
30801                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30802                     scale = targetWidth / width;
30803                 }
30804                 
30805                 if(x > 0 && y == 0){
30806                     scale = targetHeight / height;
30807                 }
30808                 
30809                 if(x > 0 && y > 0){
30810                     scale = targetWidth / width;
30811                     
30812                     if(width < height){
30813                         scale = targetHeight / height;
30814                     }
30815                 }
30816                 
30817                 context.scale(scale, scale);
30818                 
30819                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30820                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30821
30822                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30823                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30824
30825                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30826                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30827                 
30828                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30829                 
30830                 break;
30831             case 270 :
30832                 
30833                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30834                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30835                 
30836                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30837                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30838                 
30839                 var targetWidth = this.minWidth - 2 * x;
30840                 var targetHeight = this.minHeight - 2 * y;
30841                 
30842                 var scale = 1;
30843                 
30844                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30845                     scale = targetWidth / width;
30846                 }
30847                 
30848                 if(x > 0 && y == 0){
30849                     scale = targetHeight / height;
30850                 }
30851                 
30852                 if(x > 0 && y > 0){
30853                     scale = targetWidth / width;
30854                     
30855                     if(width < height){
30856                         scale = targetHeight / height;
30857                     }
30858                 }
30859                 
30860                 context.scale(scale, scale);
30861                 
30862                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30863                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30864
30865                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30866                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30867                 
30868                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30869                 
30870                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30871                 
30872                 break;
30873             default : 
30874                 break;
30875         }
30876         
30877         this.cropData = canvas.toDataURL(this.cropType);
30878         
30879         if(this.fireEvent('crop', this, this.cropData) !== false){
30880             this.process(this.file, this.cropData);
30881         }
30882         
30883         return;
30884         
30885     },
30886     
30887     setThumbBoxSize : function()
30888     {
30889         var width, height;
30890         
30891         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30892             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30893             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30894             
30895             this.minWidth = width;
30896             this.minHeight = height;
30897             
30898             if(this.rotate == 90 || this.rotate == 270){
30899                 this.minWidth = height;
30900                 this.minHeight = width;
30901             }
30902         }
30903         
30904         height = 300;
30905         width = Math.ceil(this.minWidth * height / this.minHeight);
30906         
30907         if(this.minWidth > this.minHeight){
30908             width = 300;
30909             height = Math.ceil(this.minHeight * width / this.minWidth);
30910         }
30911         
30912         this.thumbEl.setStyle({
30913             width : width + 'px',
30914             height : height + 'px'
30915         });
30916
30917         return;
30918             
30919     },
30920     
30921     setThumbBoxPosition : function()
30922     {
30923         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30924         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30925         
30926         this.thumbEl.setLeft(x);
30927         this.thumbEl.setTop(y);
30928         
30929     },
30930     
30931     baseRotateLevel : function()
30932     {
30933         this.baseRotate = 1;
30934         
30935         if(
30936                 typeof(this.exif) != 'undefined' &&
30937                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30938                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30939         ){
30940             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30941         }
30942         
30943         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30944         
30945     },
30946     
30947     baseScaleLevel : function()
30948     {
30949         var width, height;
30950         
30951         if(this.isDocument){
30952             
30953             if(this.baseRotate == 6 || this.baseRotate == 8){
30954             
30955                 height = this.thumbEl.getHeight();
30956                 this.baseScale = height / this.imageEl.OriginWidth;
30957
30958                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30959                     width = this.thumbEl.getWidth();
30960                     this.baseScale = width / this.imageEl.OriginHeight;
30961                 }
30962
30963                 return;
30964             }
30965
30966             height = this.thumbEl.getHeight();
30967             this.baseScale = height / this.imageEl.OriginHeight;
30968
30969             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30970                 width = this.thumbEl.getWidth();
30971                 this.baseScale = width / this.imageEl.OriginWidth;
30972             }
30973
30974             return;
30975         }
30976         
30977         if(this.baseRotate == 6 || this.baseRotate == 8){
30978             
30979             width = this.thumbEl.getHeight();
30980             this.baseScale = width / this.imageEl.OriginHeight;
30981             
30982             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30983                 height = this.thumbEl.getWidth();
30984                 this.baseScale = height / this.imageEl.OriginHeight;
30985             }
30986             
30987             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30988                 height = this.thumbEl.getWidth();
30989                 this.baseScale = height / this.imageEl.OriginHeight;
30990                 
30991                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30992                     width = this.thumbEl.getHeight();
30993                     this.baseScale = width / this.imageEl.OriginWidth;
30994                 }
30995             }
30996             
30997             return;
30998         }
30999         
31000         width = this.thumbEl.getWidth();
31001         this.baseScale = width / this.imageEl.OriginWidth;
31002         
31003         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31004             height = this.thumbEl.getHeight();
31005             this.baseScale = height / this.imageEl.OriginHeight;
31006         }
31007         
31008         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31009             
31010             height = this.thumbEl.getHeight();
31011             this.baseScale = height / this.imageEl.OriginHeight;
31012             
31013             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31014                 width = this.thumbEl.getWidth();
31015                 this.baseScale = width / this.imageEl.OriginWidth;
31016             }
31017             
31018         }
31019         
31020         return;
31021     },
31022     
31023     getScaleLevel : function()
31024     {
31025         return this.baseScale * Math.pow(1.1, this.scale);
31026     },
31027     
31028     onTouchStart : function(e)
31029     {
31030         if(!this.canvasLoaded){
31031             this.beforeSelectFile(e);
31032             return;
31033         }
31034         
31035         var touches = e.browserEvent.touches;
31036         
31037         if(!touches){
31038             return;
31039         }
31040         
31041         if(touches.length == 1){
31042             this.onMouseDown(e);
31043             return;
31044         }
31045         
31046         if(touches.length != 2){
31047             return;
31048         }
31049         
31050         var coords = [];
31051         
31052         for(var i = 0, finger; finger = touches[i]; i++){
31053             coords.push(finger.pageX, finger.pageY);
31054         }
31055         
31056         var x = Math.pow(coords[0] - coords[2], 2);
31057         var y = Math.pow(coords[1] - coords[3], 2);
31058         
31059         this.startDistance = Math.sqrt(x + y);
31060         
31061         this.startScale = this.scale;
31062         
31063         this.pinching = true;
31064         this.dragable = false;
31065         
31066     },
31067     
31068     onTouchMove : function(e)
31069     {
31070         if(!this.pinching && !this.dragable){
31071             return;
31072         }
31073         
31074         var touches = e.browserEvent.touches;
31075         
31076         if(!touches){
31077             return;
31078         }
31079         
31080         if(this.dragable){
31081             this.onMouseMove(e);
31082             return;
31083         }
31084         
31085         var coords = [];
31086         
31087         for(var i = 0, finger; finger = touches[i]; i++){
31088             coords.push(finger.pageX, finger.pageY);
31089         }
31090         
31091         var x = Math.pow(coords[0] - coords[2], 2);
31092         var y = Math.pow(coords[1] - coords[3], 2);
31093         
31094         this.endDistance = Math.sqrt(x + y);
31095         
31096         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31097         
31098         if(!this.zoomable()){
31099             this.scale = this.startScale;
31100             return;
31101         }
31102         
31103         this.draw();
31104         
31105     },
31106     
31107     onTouchEnd : function(e)
31108     {
31109         this.pinching = false;
31110         this.dragable = false;
31111         
31112     },
31113     
31114     process : function(file, crop)
31115     {
31116         if(this.loadMask){
31117             this.maskEl.mask(this.loadingText);
31118         }
31119         
31120         this.xhr = new XMLHttpRequest();
31121         
31122         file.xhr = this.xhr;
31123
31124         this.xhr.open(this.method, this.url, true);
31125         
31126         var headers = {
31127             "Accept": "application/json",
31128             "Cache-Control": "no-cache",
31129             "X-Requested-With": "XMLHttpRequest"
31130         };
31131         
31132         for (var headerName in headers) {
31133             var headerValue = headers[headerName];
31134             if (headerValue) {
31135                 this.xhr.setRequestHeader(headerName, headerValue);
31136             }
31137         }
31138         
31139         var _this = this;
31140         
31141         this.xhr.onload = function()
31142         {
31143             _this.xhrOnLoad(_this.xhr);
31144         }
31145         
31146         this.xhr.onerror = function()
31147         {
31148             _this.xhrOnError(_this.xhr);
31149         }
31150         
31151         var formData = new FormData();
31152
31153         formData.append('returnHTML', 'NO');
31154         
31155         if(crop){
31156             formData.append('crop', crop);
31157         }
31158         
31159         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31160             formData.append(this.paramName, file, file.name);
31161         }
31162         
31163         if(typeof(file.filename) != 'undefined'){
31164             formData.append('filename', file.filename);
31165         }
31166         
31167         if(typeof(file.mimetype) != 'undefined'){
31168             formData.append('mimetype', file.mimetype);
31169         }
31170         
31171         if(this.fireEvent('arrange', this, formData) != false){
31172             this.xhr.send(formData);
31173         };
31174     },
31175     
31176     xhrOnLoad : function(xhr)
31177     {
31178         if(this.loadMask){
31179             this.maskEl.unmask();
31180         }
31181         
31182         if (xhr.readyState !== 4) {
31183             this.fireEvent('exception', this, xhr);
31184             return;
31185         }
31186
31187         var response = Roo.decode(xhr.responseText);
31188         
31189         if(!response.success){
31190             this.fireEvent('exception', this, xhr);
31191             return;
31192         }
31193         
31194         var response = Roo.decode(xhr.responseText);
31195         
31196         this.fireEvent('upload', this, response);
31197         
31198     },
31199     
31200     xhrOnError : function()
31201     {
31202         if(this.loadMask){
31203             this.maskEl.unmask();
31204         }
31205         
31206         Roo.log('xhr on error');
31207         
31208         var response = Roo.decode(xhr.responseText);
31209           
31210         Roo.log(response);
31211         
31212     },
31213     
31214     prepare : function(file)
31215     {   
31216         if(this.loadMask){
31217             this.maskEl.mask(this.loadingText);
31218         }
31219         
31220         this.file = false;
31221         this.exif = {};
31222         
31223         if(typeof(file) === 'string'){
31224             this.loadCanvas(file);
31225             return;
31226         }
31227         
31228         if(!file || !this.urlAPI){
31229             return;
31230         }
31231         
31232         this.file = file;
31233         this.cropType = file.type;
31234         
31235         var _this = this;
31236         
31237         if(this.fireEvent('prepare', this, this.file) != false){
31238             
31239             var reader = new FileReader();
31240             
31241             reader.onload = function (e) {
31242                 if (e.target.error) {
31243                     Roo.log(e.target.error);
31244                     return;
31245                 }
31246                 
31247                 var buffer = e.target.result,
31248                     dataView = new DataView(buffer),
31249                     offset = 2,
31250                     maxOffset = dataView.byteLength - 4,
31251                     markerBytes,
31252                     markerLength;
31253                 
31254                 if (dataView.getUint16(0) === 0xffd8) {
31255                     while (offset < maxOffset) {
31256                         markerBytes = dataView.getUint16(offset);
31257                         
31258                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31259                             markerLength = dataView.getUint16(offset + 2) + 2;
31260                             if (offset + markerLength > dataView.byteLength) {
31261                                 Roo.log('Invalid meta data: Invalid segment size.');
31262                                 break;
31263                             }
31264                             
31265                             if(markerBytes == 0xffe1){
31266                                 _this.parseExifData(
31267                                     dataView,
31268                                     offset,
31269                                     markerLength
31270                                 );
31271                             }
31272                             
31273                             offset += markerLength;
31274                             
31275                             continue;
31276                         }
31277                         
31278                         break;
31279                     }
31280                     
31281                 }
31282                 
31283                 var url = _this.urlAPI.createObjectURL(_this.file);
31284                 
31285                 _this.loadCanvas(url);
31286                 
31287                 return;
31288             }
31289             
31290             reader.readAsArrayBuffer(this.file);
31291             
31292         }
31293         
31294     },
31295     
31296     parseExifData : function(dataView, offset, length)
31297     {
31298         var tiffOffset = offset + 10,
31299             littleEndian,
31300             dirOffset;
31301     
31302         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31303             // No Exif data, might be XMP data instead
31304             return;
31305         }
31306         
31307         // Check for the ASCII code for "Exif" (0x45786966):
31308         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31309             // No Exif data, might be XMP data instead
31310             return;
31311         }
31312         if (tiffOffset + 8 > dataView.byteLength) {
31313             Roo.log('Invalid Exif data: Invalid segment size.');
31314             return;
31315         }
31316         // Check for the two null bytes:
31317         if (dataView.getUint16(offset + 8) !== 0x0000) {
31318             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31319             return;
31320         }
31321         // Check the byte alignment:
31322         switch (dataView.getUint16(tiffOffset)) {
31323         case 0x4949:
31324             littleEndian = true;
31325             break;
31326         case 0x4D4D:
31327             littleEndian = false;
31328             break;
31329         default:
31330             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31331             return;
31332         }
31333         // Check for the TIFF tag marker (0x002A):
31334         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31335             Roo.log('Invalid Exif data: Missing TIFF marker.');
31336             return;
31337         }
31338         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31339         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31340         
31341         this.parseExifTags(
31342             dataView,
31343             tiffOffset,
31344             tiffOffset + dirOffset,
31345             littleEndian
31346         );
31347     },
31348     
31349     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31350     {
31351         var tagsNumber,
31352             dirEndOffset,
31353             i;
31354         if (dirOffset + 6 > dataView.byteLength) {
31355             Roo.log('Invalid Exif data: Invalid directory offset.');
31356             return;
31357         }
31358         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31359         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31360         if (dirEndOffset + 4 > dataView.byteLength) {
31361             Roo.log('Invalid Exif data: Invalid directory size.');
31362             return;
31363         }
31364         for (i = 0; i < tagsNumber; i += 1) {
31365             this.parseExifTag(
31366                 dataView,
31367                 tiffOffset,
31368                 dirOffset + 2 + 12 * i, // tag offset
31369                 littleEndian
31370             );
31371         }
31372         // Return the offset to the next directory:
31373         return dataView.getUint32(dirEndOffset, littleEndian);
31374     },
31375     
31376     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31377     {
31378         var tag = dataView.getUint16(offset, littleEndian);
31379         
31380         this.exif[tag] = this.getExifValue(
31381             dataView,
31382             tiffOffset,
31383             offset,
31384             dataView.getUint16(offset + 2, littleEndian), // tag type
31385             dataView.getUint32(offset + 4, littleEndian), // tag length
31386             littleEndian
31387         );
31388     },
31389     
31390     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31391     {
31392         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31393             tagSize,
31394             dataOffset,
31395             values,
31396             i,
31397             str,
31398             c;
31399     
31400         if (!tagType) {
31401             Roo.log('Invalid Exif data: Invalid tag type.');
31402             return;
31403         }
31404         
31405         tagSize = tagType.size * length;
31406         // Determine if the value is contained in the dataOffset bytes,
31407         // or if the value at the dataOffset is a pointer to the actual data:
31408         dataOffset = tagSize > 4 ?
31409                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31410         if (dataOffset + tagSize > dataView.byteLength) {
31411             Roo.log('Invalid Exif data: Invalid data offset.');
31412             return;
31413         }
31414         if (length === 1) {
31415             return tagType.getValue(dataView, dataOffset, littleEndian);
31416         }
31417         values = [];
31418         for (i = 0; i < length; i += 1) {
31419             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31420         }
31421         
31422         if (tagType.ascii) {
31423             str = '';
31424             // Concatenate the chars:
31425             for (i = 0; i < values.length; i += 1) {
31426                 c = values[i];
31427                 // Ignore the terminating NULL byte(s):
31428                 if (c === '\u0000') {
31429                     break;
31430                 }
31431                 str += c;
31432             }
31433             return str;
31434         }
31435         return values;
31436     }
31437     
31438 });
31439
31440 Roo.apply(Roo.bootstrap.UploadCropbox, {
31441     tags : {
31442         'Orientation': 0x0112
31443     },
31444     
31445     Orientation: {
31446             1: 0, //'top-left',
31447 //            2: 'top-right',
31448             3: 180, //'bottom-right',
31449 //            4: 'bottom-left',
31450 //            5: 'left-top',
31451             6: 90, //'right-top',
31452 //            7: 'right-bottom',
31453             8: 270 //'left-bottom'
31454     },
31455     
31456     exifTagTypes : {
31457         // byte, 8-bit unsigned int:
31458         1: {
31459             getValue: function (dataView, dataOffset) {
31460                 return dataView.getUint8(dataOffset);
31461             },
31462             size: 1
31463         },
31464         // ascii, 8-bit byte:
31465         2: {
31466             getValue: function (dataView, dataOffset) {
31467                 return String.fromCharCode(dataView.getUint8(dataOffset));
31468             },
31469             size: 1,
31470             ascii: true
31471         },
31472         // short, 16 bit int:
31473         3: {
31474             getValue: function (dataView, dataOffset, littleEndian) {
31475                 return dataView.getUint16(dataOffset, littleEndian);
31476             },
31477             size: 2
31478         },
31479         // long, 32 bit int:
31480         4: {
31481             getValue: function (dataView, dataOffset, littleEndian) {
31482                 return dataView.getUint32(dataOffset, littleEndian);
31483             },
31484             size: 4
31485         },
31486         // rational = two long values, first is numerator, second is denominator:
31487         5: {
31488             getValue: function (dataView, dataOffset, littleEndian) {
31489                 return dataView.getUint32(dataOffset, littleEndian) /
31490                     dataView.getUint32(dataOffset + 4, littleEndian);
31491             },
31492             size: 8
31493         },
31494         // slong, 32 bit signed int:
31495         9: {
31496             getValue: function (dataView, dataOffset, littleEndian) {
31497                 return dataView.getInt32(dataOffset, littleEndian);
31498             },
31499             size: 4
31500         },
31501         // srational, two slongs, first is numerator, second is denominator:
31502         10: {
31503             getValue: function (dataView, dataOffset, littleEndian) {
31504                 return dataView.getInt32(dataOffset, littleEndian) /
31505                     dataView.getInt32(dataOffset + 4, littleEndian);
31506             },
31507             size: 8
31508         }
31509     },
31510     
31511     footer : {
31512         STANDARD : [
31513             {
31514                 tag : 'div',
31515                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31516                 action : 'rotate-left',
31517                 cn : [
31518                     {
31519                         tag : 'button',
31520                         cls : 'btn btn-default',
31521                         html : '<i class="fa fa-undo"></i>'
31522                     }
31523                 ]
31524             },
31525             {
31526                 tag : 'div',
31527                 cls : 'btn-group roo-upload-cropbox-picture',
31528                 action : 'picture',
31529                 cn : [
31530                     {
31531                         tag : 'button',
31532                         cls : 'btn btn-default',
31533                         html : '<i class="fa fa-picture-o"></i>'
31534                     }
31535                 ]
31536             },
31537             {
31538                 tag : 'div',
31539                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31540                 action : 'rotate-right',
31541                 cn : [
31542                     {
31543                         tag : 'button',
31544                         cls : 'btn btn-default',
31545                         html : '<i class="fa fa-repeat"></i>'
31546                     }
31547                 ]
31548             }
31549         ],
31550         DOCUMENT : [
31551             {
31552                 tag : 'div',
31553                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31554                 action : 'rotate-left',
31555                 cn : [
31556                     {
31557                         tag : 'button',
31558                         cls : 'btn btn-default',
31559                         html : '<i class="fa fa-undo"></i>'
31560                     }
31561                 ]
31562             },
31563             {
31564                 tag : 'div',
31565                 cls : 'btn-group roo-upload-cropbox-download',
31566                 action : 'download',
31567                 cn : [
31568                     {
31569                         tag : 'button',
31570                         cls : 'btn btn-default',
31571                         html : '<i class="fa fa-download"></i>'
31572                     }
31573                 ]
31574             },
31575             {
31576                 tag : 'div',
31577                 cls : 'btn-group roo-upload-cropbox-crop',
31578                 action : 'crop',
31579                 cn : [
31580                     {
31581                         tag : 'button',
31582                         cls : 'btn btn-default',
31583                         html : '<i class="fa fa-crop"></i>'
31584                     }
31585                 ]
31586             },
31587             {
31588                 tag : 'div',
31589                 cls : 'btn-group roo-upload-cropbox-trash',
31590                 action : 'trash',
31591                 cn : [
31592                     {
31593                         tag : 'button',
31594                         cls : 'btn btn-default',
31595                         html : '<i class="fa fa-trash"></i>'
31596                     }
31597                 ]
31598             },
31599             {
31600                 tag : 'div',
31601                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31602                 action : 'rotate-right',
31603                 cn : [
31604                     {
31605                         tag : 'button',
31606                         cls : 'btn btn-default',
31607                         html : '<i class="fa fa-repeat"></i>'
31608                     }
31609                 ]
31610             }
31611         ],
31612         ROTATOR : [
31613             {
31614                 tag : 'div',
31615                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31616                 action : 'rotate-left',
31617                 cn : [
31618                     {
31619                         tag : 'button',
31620                         cls : 'btn btn-default',
31621                         html : '<i class="fa fa-undo"></i>'
31622                     }
31623                 ]
31624             },
31625             {
31626                 tag : 'div',
31627                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31628                 action : 'rotate-right',
31629                 cn : [
31630                     {
31631                         tag : 'button',
31632                         cls : 'btn btn-default',
31633                         html : '<i class="fa fa-repeat"></i>'
31634                     }
31635                 ]
31636             }
31637         ]
31638     }
31639 });
31640
31641 /*
31642 * Licence: LGPL
31643 */
31644
31645 /**
31646  * @class Roo.bootstrap.DocumentManager
31647  * @extends Roo.bootstrap.Component
31648  * Bootstrap DocumentManager class
31649  * @cfg {String} paramName default 'imageUpload'
31650  * @cfg {String} toolTipName default 'filename'
31651  * @cfg {String} method default POST
31652  * @cfg {String} url action url
31653  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31654  * @cfg {Boolean} multiple multiple upload default true
31655  * @cfg {Number} thumbSize default 300
31656  * @cfg {String} fieldLabel
31657  * @cfg {Number} labelWidth default 4
31658  * @cfg {String} labelAlign (left|top) default left
31659  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31660 * @cfg {Number} labellg set the width of label (1-12)
31661  * @cfg {Number} labelmd set the width of label (1-12)
31662  * @cfg {Number} labelsm set the width of label (1-12)
31663  * @cfg {Number} labelxs set the width of label (1-12)
31664  * 
31665  * @constructor
31666  * Create a new DocumentManager
31667  * @param {Object} config The config object
31668  */
31669
31670 Roo.bootstrap.DocumentManager = function(config){
31671     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31672     
31673     this.files = [];
31674     this.delegates = [];
31675     
31676     this.addEvents({
31677         /**
31678          * @event initial
31679          * Fire when initial the DocumentManager
31680          * @param {Roo.bootstrap.DocumentManager} this
31681          */
31682         "initial" : true,
31683         /**
31684          * @event inspect
31685          * inspect selected file
31686          * @param {Roo.bootstrap.DocumentManager} this
31687          * @param {File} file
31688          */
31689         "inspect" : true,
31690         /**
31691          * @event exception
31692          * Fire when xhr load exception
31693          * @param {Roo.bootstrap.DocumentManager} this
31694          * @param {XMLHttpRequest} xhr
31695          */
31696         "exception" : true,
31697         /**
31698          * @event afterupload
31699          * Fire when xhr load exception
31700          * @param {Roo.bootstrap.DocumentManager} this
31701          * @param {XMLHttpRequest} xhr
31702          */
31703         "afterupload" : true,
31704         /**
31705          * @event prepare
31706          * prepare the form data
31707          * @param {Roo.bootstrap.DocumentManager} this
31708          * @param {Object} formData
31709          */
31710         "prepare" : true,
31711         /**
31712          * @event remove
31713          * Fire when remove the file
31714          * @param {Roo.bootstrap.DocumentManager} this
31715          * @param {Object} file
31716          */
31717         "remove" : true,
31718         /**
31719          * @event refresh
31720          * Fire after refresh the file
31721          * @param {Roo.bootstrap.DocumentManager} this
31722          */
31723         "refresh" : true,
31724         /**
31725          * @event click
31726          * Fire after click the image
31727          * @param {Roo.bootstrap.DocumentManager} this
31728          * @param {Object} file
31729          */
31730         "click" : true,
31731         /**
31732          * @event edit
31733          * Fire when upload a image and editable set to true
31734          * @param {Roo.bootstrap.DocumentManager} this
31735          * @param {Object} file
31736          */
31737         "edit" : true,
31738         /**
31739          * @event beforeselectfile
31740          * Fire before select file
31741          * @param {Roo.bootstrap.DocumentManager} this
31742          */
31743         "beforeselectfile" : true,
31744         /**
31745          * @event process
31746          * Fire before process file
31747          * @param {Roo.bootstrap.DocumentManager} this
31748          * @param {Object} file
31749          */
31750         "process" : true,
31751         /**
31752          * @event previewrendered
31753          * Fire when preview rendered
31754          * @param {Roo.bootstrap.DocumentManager} this
31755          * @param {Object} file
31756          */
31757         "previewrendered" : true,
31758         /**
31759          */
31760         "previewResize" : true
31761         
31762     });
31763 };
31764
31765 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31766     
31767     boxes : 0,
31768     inputName : '',
31769     thumbSize : 300,
31770     multiple : true,
31771     files : false,
31772     method : 'POST',
31773     url : '',
31774     paramName : 'imageUpload',
31775     toolTipName : 'filename',
31776     fieldLabel : '',
31777     labelWidth : 4,
31778     labelAlign : 'left',
31779     editable : true,
31780     delegates : false,
31781     xhr : false, 
31782     
31783     labellg : 0,
31784     labelmd : 0,
31785     labelsm : 0,
31786     labelxs : 0,
31787     
31788     getAutoCreate : function()
31789     {   
31790         var managerWidget = {
31791             tag : 'div',
31792             cls : 'roo-document-manager',
31793             cn : [
31794                 {
31795                     tag : 'input',
31796                     cls : 'roo-document-manager-selector',
31797                     type : 'file'
31798                 },
31799                 {
31800                     tag : 'div',
31801                     cls : 'roo-document-manager-uploader',
31802                     cn : [
31803                         {
31804                             tag : 'div',
31805                             cls : 'roo-document-manager-upload-btn',
31806                             html : '<i class="fa fa-plus"></i>'
31807                         }
31808                     ]
31809                     
31810                 }
31811             ]
31812         };
31813         
31814         var content = [
31815             {
31816                 tag : 'div',
31817                 cls : 'column col-md-12',
31818                 cn : managerWidget
31819             }
31820         ];
31821         
31822         if(this.fieldLabel.length){
31823             
31824             content = [
31825                 {
31826                     tag : 'div',
31827                     cls : 'column col-md-12',
31828                     html : this.fieldLabel
31829                 },
31830                 {
31831                     tag : 'div',
31832                     cls : 'column col-md-12',
31833                     cn : managerWidget
31834                 }
31835             ];
31836
31837             if(this.labelAlign == 'left'){
31838                 content = [
31839                     {
31840                         tag : 'div',
31841                         cls : 'column',
31842                         html : this.fieldLabel
31843                     },
31844                     {
31845                         tag : 'div',
31846                         cls : 'column',
31847                         cn : managerWidget
31848                     }
31849                 ];
31850                 
31851                 if(this.labelWidth > 12){
31852                     content[0].style = "width: " + this.labelWidth + 'px';
31853                 }
31854
31855                 if(this.labelWidth < 13 && this.labelmd == 0){
31856                     this.labelmd = this.labelWidth;
31857                 }
31858
31859                 if(this.labellg > 0){
31860                     content[0].cls += ' col-lg-' + this.labellg;
31861                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31862                 }
31863
31864                 if(this.labelmd > 0){
31865                     content[0].cls += ' col-md-' + this.labelmd;
31866                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31867                 }
31868
31869                 if(this.labelsm > 0){
31870                     content[0].cls += ' col-sm-' + this.labelsm;
31871                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31872                 }
31873
31874                 if(this.labelxs > 0){
31875                     content[0].cls += ' col-xs-' + this.labelxs;
31876                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31877                 }
31878                 
31879             }
31880         }
31881         
31882         var cfg = {
31883             tag : 'div',
31884             cls : 'row clearfix',
31885             cn : content
31886         };
31887         
31888         return cfg;
31889         
31890     },
31891     
31892     initEvents : function()
31893     {
31894         this.managerEl = this.el.select('.roo-document-manager', true).first();
31895         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31896         
31897         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31898         this.selectorEl.hide();
31899         
31900         if(this.multiple){
31901             this.selectorEl.attr('multiple', 'multiple');
31902         }
31903         
31904         this.selectorEl.on('change', this.onFileSelected, this);
31905         
31906         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31907         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31908         
31909         this.uploader.on('click', this.onUploaderClick, this);
31910         
31911         this.renderProgressDialog();
31912         
31913         var _this = this;
31914         
31915         window.addEventListener("resize", function() { _this.refresh(); } );
31916         
31917         this.fireEvent('initial', this);
31918     },
31919     
31920     renderProgressDialog : function()
31921     {
31922         var _this = this;
31923         
31924         this.progressDialog = new Roo.bootstrap.Modal({
31925             cls : 'roo-document-manager-progress-dialog',
31926             allow_close : false,
31927             animate : false,
31928             title : '',
31929             buttons : [
31930                 {
31931                     name  :'cancel',
31932                     weight : 'danger',
31933                     html : 'Cancel'
31934                 }
31935             ], 
31936             listeners : { 
31937                 btnclick : function() {
31938                     _this.uploadCancel();
31939                     this.hide();
31940                 }
31941             }
31942         });
31943          
31944         this.progressDialog.render(Roo.get(document.body));
31945          
31946         this.progress = new Roo.bootstrap.Progress({
31947             cls : 'roo-document-manager-progress',
31948             active : true,
31949             striped : true
31950         });
31951         
31952         this.progress.render(this.progressDialog.getChildContainer());
31953         
31954         this.progressBar = new Roo.bootstrap.ProgressBar({
31955             cls : 'roo-document-manager-progress-bar',
31956             aria_valuenow : 0,
31957             aria_valuemin : 0,
31958             aria_valuemax : 12,
31959             panel : 'success'
31960         });
31961         
31962         this.progressBar.render(this.progress.getChildContainer());
31963     },
31964     
31965     onUploaderClick : function(e)
31966     {
31967         e.preventDefault();
31968      
31969         if(this.fireEvent('beforeselectfile', this) != false){
31970             this.selectorEl.dom.click();
31971         }
31972         
31973     },
31974     
31975     onFileSelected : function(e)
31976     {
31977         e.preventDefault();
31978         
31979         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31980             return;
31981         }
31982         
31983         Roo.each(this.selectorEl.dom.files, function(file){
31984             if(this.fireEvent('inspect', this, file) != false){
31985                 this.files.push(file);
31986             }
31987         }, this);
31988         
31989         this.queue();
31990         
31991     },
31992     
31993     queue : function()
31994     {
31995         this.selectorEl.dom.value = '';
31996         
31997         if(!this.files || !this.files.length){
31998             return;
31999         }
32000         
32001         if(this.boxes > 0 && this.files.length > this.boxes){
32002             this.files = this.files.slice(0, this.boxes);
32003         }
32004         
32005         this.uploader.show();
32006         
32007         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32008             this.uploader.hide();
32009         }
32010         
32011         var _this = this;
32012         
32013         var files = [];
32014         
32015         var docs = [];
32016         
32017         Roo.each(this.files, function(file){
32018             
32019             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32020                 var f = this.renderPreview(file);
32021                 files.push(f);
32022                 return;
32023             }
32024             
32025             if(file.type.indexOf('image') != -1){
32026                 this.delegates.push(
32027                     (function(){
32028                         _this.process(file);
32029                     }).createDelegate(this)
32030                 );
32031         
32032                 return;
32033             }
32034             
32035             docs.push(
32036                 (function(){
32037                     _this.process(file);
32038                 }).createDelegate(this)
32039             );
32040             
32041         }, this);
32042         
32043         this.files = files;
32044         
32045         this.delegates = this.delegates.concat(docs);
32046         
32047         if(!this.delegates.length){
32048             this.refresh();
32049             return;
32050         }
32051         
32052         this.progressBar.aria_valuemax = this.delegates.length;
32053         
32054         this.arrange();
32055         
32056         return;
32057     },
32058     
32059     arrange : function()
32060     {
32061         if(!this.delegates.length){
32062             this.progressDialog.hide();
32063             this.refresh();
32064             return;
32065         }
32066         
32067         var delegate = this.delegates.shift();
32068         
32069         this.progressDialog.show();
32070         
32071         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32072         
32073         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32074         
32075         delegate();
32076     },
32077     
32078     refresh : function()
32079     {
32080         this.uploader.show();
32081         
32082         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32083             this.uploader.hide();
32084         }
32085         
32086         Roo.isTouch ? this.closable(false) : this.closable(true);
32087         
32088         this.fireEvent('refresh', this);
32089     },
32090     
32091     onRemove : function(e, el, o)
32092     {
32093         e.preventDefault();
32094         
32095         this.fireEvent('remove', this, o);
32096         
32097     },
32098     
32099     remove : function(o)
32100     {
32101         var files = [];
32102         
32103         Roo.each(this.files, function(file){
32104             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32105                 files.push(file);
32106                 return;
32107             }
32108
32109             o.target.remove();
32110
32111         }, this);
32112         
32113         this.files = files;
32114         
32115         this.refresh();
32116     },
32117     
32118     clear : function()
32119     {
32120         Roo.each(this.files, function(file){
32121             if(!file.target){
32122                 return;
32123             }
32124             
32125             file.target.remove();
32126
32127         }, this);
32128         
32129         this.files = [];
32130         
32131         this.refresh();
32132     },
32133     
32134     onClick : function(e, el, o)
32135     {
32136         e.preventDefault();
32137         
32138         this.fireEvent('click', this, o);
32139         
32140     },
32141     
32142     closable : function(closable)
32143     {
32144         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32145             
32146             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32147             
32148             if(closable){
32149                 el.show();
32150                 return;
32151             }
32152             
32153             el.hide();
32154             
32155         }, this);
32156     },
32157     
32158     xhrOnLoad : function(xhr)
32159     {
32160         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32161             el.remove();
32162         }, this);
32163         
32164         if (xhr.readyState !== 4) {
32165             this.arrange();
32166             this.fireEvent('exception', this, xhr);
32167             return;
32168         }
32169
32170         var response = Roo.decode(xhr.responseText);
32171         
32172         if(!response.success){
32173             this.arrange();
32174             this.fireEvent('exception', this, xhr);
32175             return;
32176         }
32177         
32178         var file = this.renderPreview(response.data);
32179         
32180         this.files.push(file);
32181         
32182         this.arrange();
32183         
32184         this.fireEvent('afterupload', this, xhr);
32185         
32186     },
32187     
32188     xhrOnError : function(xhr)
32189     {
32190         Roo.log('xhr on error');
32191         
32192         var response = Roo.decode(xhr.responseText);
32193           
32194         Roo.log(response);
32195         
32196         this.arrange();
32197     },
32198     
32199     process : function(file)
32200     {
32201         if(this.fireEvent('process', this, file) !== false){
32202             if(this.editable && file.type.indexOf('image') != -1){
32203                 this.fireEvent('edit', this, file);
32204                 return;
32205             }
32206
32207             this.uploadStart(file, false);
32208
32209             return;
32210         }
32211         
32212     },
32213     
32214     uploadStart : function(file, crop)
32215     {
32216         this.xhr = new XMLHttpRequest();
32217         
32218         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32219             this.arrange();
32220             return;
32221         }
32222         
32223         file.xhr = this.xhr;
32224             
32225         this.managerEl.createChild({
32226             tag : 'div',
32227             cls : 'roo-document-manager-loading',
32228             cn : [
32229                 {
32230                     tag : 'div',
32231                     tooltip : file.name,
32232                     cls : 'roo-document-manager-thumb',
32233                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32234                 }
32235             ]
32236
32237         });
32238
32239         this.xhr.open(this.method, this.url, true);
32240         
32241         var headers = {
32242             "Accept": "application/json",
32243             "Cache-Control": "no-cache",
32244             "X-Requested-With": "XMLHttpRequest"
32245         };
32246         
32247         for (var headerName in headers) {
32248             var headerValue = headers[headerName];
32249             if (headerValue) {
32250                 this.xhr.setRequestHeader(headerName, headerValue);
32251             }
32252         }
32253         
32254         var _this = this;
32255         
32256         this.xhr.onload = function()
32257         {
32258             _this.xhrOnLoad(_this.xhr);
32259         }
32260         
32261         this.xhr.onerror = function()
32262         {
32263             _this.xhrOnError(_this.xhr);
32264         }
32265         
32266         var formData = new FormData();
32267
32268         formData.append('returnHTML', 'NO');
32269         
32270         if(crop){
32271             formData.append('crop', crop);
32272         }
32273         
32274         formData.append(this.paramName, file, file.name);
32275         
32276         var options = {
32277             file : file, 
32278             manually : false
32279         };
32280         
32281         if(this.fireEvent('prepare', this, formData, options) != false){
32282             
32283             if(options.manually){
32284                 return;
32285             }
32286             
32287             this.xhr.send(formData);
32288             return;
32289         };
32290         
32291         this.uploadCancel();
32292     },
32293     
32294     uploadCancel : function()
32295     {
32296         if (this.xhr) {
32297             this.xhr.abort();
32298         }
32299         
32300         this.delegates = [];
32301         
32302         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32303             el.remove();
32304         }, this);
32305         
32306         this.arrange();
32307     },
32308     
32309     renderPreview : function(file)
32310     {
32311         if(typeof(file.target) != 'undefined' && file.target){
32312             return file;
32313         }
32314         
32315         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32316         
32317         var previewEl = this.managerEl.createChild({
32318             tag : 'div',
32319             cls : 'roo-document-manager-preview',
32320             cn : [
32321                 {
32322                     tag : 'div',
32323                     tooltip : file[this.toolTipName],
32324                     cls : 'roo-document-manager-thumb',
32325                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32326                 },
32327                 {
32328                     tag : 'button',
32329                     cls : 'close',
32330                     html : '<i class="fa fa-times-circle"></i>'
32331                 }
32332             ]
32333         });
32334
32335         var close = previewEl.select('button.close', true).first();
32336
32337         close.on('click', this.onRemove, this, file);
32338
32339         file.target = previewEl;
32340
32341         var image = previewEl.select('img', true).first();
32342         
32343         var _this = this;
32344         
32345         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32346         
32347         image.on('click', this.onClick, this, file);
32348         
32349         this.fireEvent('previewrendered', this, file);
32350         
32351         return file;
32352         
32353     },
32354     
32355     onPreviewLoad : function(file, image)
32356     {
32357         if(typeof(file.target) == 'undefined' || !file.target){
32358             return;
32359         }
32360         
32361         var width = image.dom.naturalWidth || image.dom.width;
32362         var height = image.dom.naturalHeight || image.dom.height;
32363         
32364         if(!this.previewResize) {
32365             return;
32366         }
32367         
32368         if(width > height){
32369             file.target.addClass('wide');
32370             return;
32371         }
32372         
32373         file.target.addClass('tall');
32374         return;
32375         
32376     },
32377     
32378     uploadFromSource : function(file, crop)
32379     {
32380         this.xhr = new XMLHttpRequest();
32381         
32382         this.managerEl.createChild({
32383             tag : 'div',
32384             cls : 'roo-document-manager-loading',
32385             cn : [
32386                 {
32387                     tag : 'div',
32388                     tooltip : file.name,
32389                     cls : 'roo-document-manager-thumb',
32390                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32391                 }
32392             ]
32393
32394         });
32395
32396         this.xhr.open(this.method, this.url, true);
32397         
32398         var headers = {
32399             "Accept": "application/json",
32400             "Cache-Control": "no-cache",
32401             "X-Requested-With": "XMLHttpRequest"
32402         };
32403         
32404         for (var headerName in headers) {
32405             var headerValue = headers[headerName];
32406             if (headerValue) {
32407                 this.xhr.setRequestHeader(headerName, headerValue);
32408             }
32409         }
32410         
32411         var _this = this;
32412         
32413         this.xhr.onload = function()
32414         {
32415             _this.xhrOnLoad(_this.xhr);
32416         }
32417         
32418         this.xhr.onerror = function()
32419         {
32420             _this.xhrOnError(_this.xhr);
32421         }
32422         
32423         var formData = new FormData();
32424
32425         formData.append('returnHTML', 'NO');
32426         
32427         formData.append('crop', crop);
32428         
32429         if(typeof(file.filename) != 'undefined'){
32430             formData.append('filename', file.filename);
32431         }
32432         
32433         if(typeof(file.mimetype) != 'undefined'){
32434             formData.append('mimetype', file.mimetype);
32435         }
32436         
32437         Roo.log(formData);
32438         
32439         if(this.fireEvent('prepare', this, formData) != false){
32440             this.xhr.send(formData);
32441         };
32442     }
32443 });
32444
32445 /*
32446 * Licence: LGPL
32447 */
32448
32449 /**
32450  * @class Roo.bootstrap.DocumentViewer
32451  * @extends Roo.bootstrap.Component
32452  * Bootstrap DocumentViewer class
32453  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32454  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32455  * 
32456  * @constructor
32457  * Create a new DocumentViewer
32458  * @param {Object} config The config object
32459  */
32460
32461 Roo.bootstrap.DocumentViewer = function(config){
32462     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32463     
32464     this.addEvents({
32465         /**
32466          * @event initial
32467          * Fire after initEvent
32468          * @param {Roo.bootstrap.DocumentViewer} this
32469          */
32470         "initial" : true,
32471         /**
32472          * @event click
32473          * Fire after click
32474          * @param {Roo.bootstrap.DocumentViewer} this
32475          */
32476         "click" : true,
32477         /**
32478          * @event download
32479          * Fire after download button
32480          * @param {Roo.bootstrap.DocumentViewer} this
32481          */
32482         "download" : true,
32483         /**
32484          * @event trash
32485          * Fire after trash button
32486          * @param {Roo.bootstrap.DocumentViewer} this
32487          */
32488         "trash" : true
32489         
32490     });
32491 };
32492
32493 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32494     
32495     showDownload : true,
32496     
32497     showTrash : true,
32498     
32499     getAutoCreate : function()
32500     {
32501         var cfg = {
32502             tag : 'div',
32503             cls : 'roo-document-viewer',
32504             cn : [
32505                 {
32506                     tag : 'div',
32507                     cls : 'roo-document-viewer-body',
32508                     cn : [
32509                         {
32510                             tag : 'div',
32511                             cls : 'roo-document-viewer-thumb',
32512                             cn : [
32513                                 {
32514                                     tag : 'img',
32515                                     cls : 'roo-document-viewer-image'
32516                                 }
32517                             ]
32518                         }
32519                     ]
32520                 },
32521                 {
32522                     tag : 'div',
32523                     cls : 'roo-document-viewer-footer',
32524                     cn : {
32525                         tag : 'div',
32526                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32527                         cn : [
32528                             {
32529                                 tag : 'div',
32530                                 cls : 'btn-group roo-document-viewer-download',
32531                                 cn : [
32532                                     {
32533                                         tag : 'button',
32534                                         cls : 'btn btn-default',
32535                                         html : '<i class="fa fa-download"></i>'
32536                                     }
32537                                 ]
32538                             },
32539                             {
32540                                 tag : 'div',
32541                                 cls : 'btn-group roo-document-viewer-trash',
32542                                 cn : [
32543                                     {
32544                                         tag : 'button',
32545                                         cls : 'btn btn-default',
32546                                         html : '<i class="fa fa-trash"></i>'
32547                                     }
32548                                 ]
32549                             }
32550                         ]
32551                     }
32552                 }
32553             ]
32554         };
32555         
32556         return cfg;
32557     },
32558     
32559     initEvents : function()
32560     {
32561         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32562         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32563         
32564         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32565         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32566         
32567         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32568         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32569         
32570         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32571         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32572         
32573         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32574         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32575         
32576         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32577         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32578         
32579         this.bodyEl.on('click', this.onClick, this);
32580         this.downloadBtn.on('click', this.onDownload, this);
32581         this.trashBtn.on('click', this.onTrash, this);
32582         
32583         this.downloadBtn.hide();
32584         this.trashBtn.hide();
32585         
32586         if(this.showDownload){
32587             this.downloadBtn.show();
32588         }
32589         
32590         if(this.showTrash){
32591             this.trashBtn.show();
32592         }
32593         
32594         if(!this.showDownload && !this.showTrash) {
32595             this.footerEl.hide();
32596         }
32597         
32598     },
32599     
32600     initial : function()
32601     {
32602         this.fireEvent('initial', this);
32603         
32604     },
32605     
32606     onClick : function(e)
32607     {
32608         e.preventDefault();
32609         
32610         this.fireEvent('click', this);
32611     },
32612     
32613     onDownload : function(e)
32614     {
32615         e.preventDefault();
32616         
32617         this.fireEvent('download', this);
32618     },
32619     
32620     onTrash : function(e)
32621     {
32622         e.preventDefault();
32623         
32624         this.fireEvent('trash', this);
32625     }
32626     
32627 });
32628 /*
32629  * - LGPL
32630  *
32631  * nav progress bar
32632  * 
32633  */
32634
32635 /**
32636  * @class Roo.bootstrap.NavProgressBar
32637  * @extends Roo.bootstrap.Component
32638  * Bootstrap NavProgressBar class
32639  * 
32640  * @constructor
32641  * Create a new nav progress bar
32642  * @param {Object} config The config object
32643  */
32644
32645 Roo.bootstrap.NavProgressBar = function(config){
32646     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32647
32648     this.bullets = this.bullets || [];
32649    
32650 //    Roo.bootstrap.NavProgressBar.register(this);
32651      this.addEvents({
32652         /**
32653              * @event changed
32654              * Fires when the active item changes
32655              * @param {Roo.bootstrap.NavProgressBar} this
32656              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32657              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32658          */
32659         'changed': true
32660      });
32661     
32662 };
32663
32664 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32665     
32666     bullets : [],
32667     barItems : [],
32668     
32669     getAutoCreate : function()
32670     {
32671         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32672         
32673         cfg = {
32674             tag : 'div',
32675             cls : 'roo-navigation-bar-group',
32676             cn : [
32677                 {
32678                     tag : 'div',
32679                     cls : 'roo-navigation-top-bar'
32680                 },
32681                 {
32682                     tag : 'div',
32683                     cls : 'roo-navigation-bullets-bar',
32684                     cn : [
32685                         {
32686                             tag : 'ul',
32687                             cls : 'roo-navigation-bar'
32688                         }
32689                     ]
32690                 },
32691                 
32692                 {
32693                     tag : 'div',
32694                     cls : 'roo-navigation-bottom-bar'
32695                 }
32696             ]
32697             
32698         };
32699         
32700         return cfg;
32701         
32702     },
32703     
32704     initEvents: function() 
32705     {
32706         
32707     },
32708     
32709     onRender : function(ct, position) 
32710     {
32711         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32712         
32713         if(this.bullets.length){
32714             Roo.each(this.bullets, function(b){
32715                this.addItem(b);
32716             }, this);
32717         }
32718         
32719         this.format();
32720         
32721     },
32722     
32723     addItem : function(cfg)
32724     {
32725         var item = new Roo.bootstrap.NavProgressItem(cfg);
32726         
32727         item.parentId = this.id;
32728         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32729         
32730         if(cfg.html){
32731             var top = new Roo.bootstrap.Element({
32732                 tag : 'div',
32733                 cls : 'roo-navigation-bar-text'
32734             });
32735             
32736             var bottom = new Roo.bootstrap.Element({
32737                 tag : 'div',
32738                 cls : 'roo-navigation-bar-text'
32739             });
32740             
32741             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32742             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32743             
32744             var topText = new Roo.bootstrap.Element({
32745                 tag : 'span',
32746                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32747             });
32748             
32749             var bottomText = new Roo.bootstrap.Element({
32750                 tag : 'span',
32751                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32752             });
32753             
32754             topText.onRender(top.el, null);
32755             bottomText.onRender(bottom.el, null);
32756             
32757             item.topEl = top;
32758             item.bottomEl = bottom;
32759         }
32760         
32761         this.barItems.push(item);
32762         
32763         return item;
32764     },
32765     
32766     getActive : function()
32767     {
32768         var active = false;
32769         
32770         Roo.each(this.barItems, function(v){
32771             
32772             if (!v.isActive()) {
32773                 return;
32774             }
32775             
32776             active = v;
32777             return false;
32778             
32779         });
32780         
32781         return active;
32782     },
32783     
32784     setActiveItem : function(item)
32785     {
32786         var prev = false;
32787         
32788         Roo.each(this.barItems, function(v){
32789             if (v.rid == item.rid) {
32790                 return ;
32791             }
32792             
32793             if (v.isActive()) {
32794                 v.setActive(false);
32795                 prev = v;
32796             }
32797         });
32798
32799         item.setActive(true);
32800         
32801         this.fireEvent('changed', this, item, prev);
32802     },
32803     
32804     getBarItem: function(rid)
32805     {
32806         var ret = false;
32807         
32808         Roo.each(this.barItems, function(e) {
32809             if (e.rid != rid) {
32810                 return;
32811             }
32812             
32813             ret =  e;
32814             return false;
32815         });
32816         
32817         return ret;
32818     },
32819     
32820     indexOfItem : function(item)
32821     {
32822         var index = false;
32823         
32824         Roo.each(this.barItems, function(v, i){
32825             
32826             if (v.rid != item.rid) {
32827                 return;
32828             }
32829             
32830             index = i;
32831             return false
32832         });
32833         
32834         return index;
32835     },
32836     
32837     setActiveNext : function()
32838     {
32839         var i = this.indexOfItem(this.getActive());
32840         
32841         if (i > this.barItems.length) {
32842             return;
32843         }
32844         
32845         this.setActiveItem(this.barItems[i+1]);
32846     },
32847     
32848     setActivePrev : function()
32849     {
32850         var i = this.indexOfItem(this.getActive());
32851         
32852         if (i  < 1) {
32853             return;
32854         }
32855         
32856         this.setActiveItem(this.barItems[i-1]);
32857     },
32858     
32859     format : function()
32860     {
32861         if(!this.barItems.length){
32862             return;
32863         }
32864      
32865         var width = 100 / this.barItems.length;
32866         
32867         Roo.each(this.barItems, function(i){
32868             i.el.setStyle('width', width + '%');
32869             i.topEl.el.setStyle('width', width + '%');
32870             i.bottomEl.el.setStyle('width', width + '%');
32871         }, this);
32872         
32873     }
32874     
32875 });
32876 /*
32877  * - LGPL
32878  *
32879  * Nav Progress Item
32880  * 
32881  */
32882
32883 /**
32884  * @class Roo.bootstrap.NavProgressItem
32885  * @extends Roo.bootstrap.Component
32886  * Bootstrap NavProgressItem class
32887  * @cfg {String} rid the reference id
32888  * @cfg {Boolean} active (true|false) Is item active default false
32889  * @cfg {Boolean} disabled (true|false) Is item active default false
32890  * @cfg {String} html
32891  * @cfg {String} position (top|bottom) text position default bottom
32892  * @cfg {String} icon show icon instead of number
32893  * 
32894  * @constructor
32895  * Create a new NavProgressItem
32896  * @param {Object} config The config object
32897  */
32898 Roo.bootstrap.NavProgressItem = function(config){
32899     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32900     this.addEvents({
32901         // raw events
32902         /**
32903          * @event click
32904          * The raw click event for the entire grid.
32905          * @param {Roo.bootstrap.NavProgressItem} this
32906          * @param {Roo.EventObject} e
32907          */
32908         "click" : true
32909     });
32910    
32911 };
32912
32913 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32914     
32915     rid : '',
32916     active : false,
32917     disabled : false,
32918     html : '',
32919     position : 'bottom',
32920     icon : false,
32921     
32922     getAutoCreate : function()
32923     {
32924         var iconCls = 'roo-navigation-bar-item-icon';
32925         
32926         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32927         
32928         var cfg = {
32929             tag: 'li',
32930             cls: 'roo-navigation-bar-item',
32931             cn : [
32932                 {
32933                     tag : 'i',
32934                     cls : iconCls
32935                 }
32936             ]
32937         };
32938         
32939         if(this.active){
32940             cfg.cls += ' active';
32941         }
32942         if(this.disabled){
32943             cfg.cls += ' disabled';
32944         }
32945         
32946         return cfg;
32947     },
32948     
32949     disable : function()
32950     {
32951         this.setDisabled(true);
32952     },
32953     
32954     enable : function()
32955     {
32956         this.setDisabled(false);
32957     },
32958     
32959     initEvents: function() 
32960     {
32961         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32962         
32963         this.iconEl.on('click', this.onClick, this);
32964     },
32965     
32966     onClick : function(e)
32967     {
32968         e.preventDefault();
32969         
32970         if(this.disabled){
32971             return;
32972         }
32973         
32974         if(this.fireEvent('click', this, e) === false){
32975             return;
32976         };
32977         
32978         this.parent().setActiveItem(this);
32979     },
32980     
32981     isActive: function () 
32982     {
32983         return this.active;
32984     },
32985     
32986     setActive : function(state)
32987     {
32988         if(this.active == state){
32989             return;
32990         }
32991         
32992         this.active = state;
32993         
32994         if (state) {
32995             this.el.addClass('active');
32996             return;
32997         }
32998         
32999         this.el.removeClass('active');
33000         
33001         return;
33002     },
33003     
33004     setDisabled : function(state)
33005     {
33006         if(this.disabled == state){
33007             return;
33008         }
33009         
33010         this.disabled = state;
33011         
33012         if (state) {
33013             this.el.addClass('disabled');
33014             return;
33015         }
33016         
33017         this.el.removeClass('disabled');
33018     },
33019     
33020     tooltipEl : function()
33021     {
33022         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33023     }
33024 });
33025  
33026
33027  /*
33028  * - LGPL
33029  *
33030  * FieldLabel
33031  * 
33032  */
33033
33034 /**
33035  * @class Roo.bootstrap.FieldLabel
33036  * @extends Roo.bootstrap.Component
33037  * Bootstrap FieldLabel class
33038  * @cfg {String} html contents of the element
33039  * @cfg {String} tag tag of the element default label
33040  * @cfg {String} cls class of the element
33041  * @cfg {String} target label target 
33042  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33043  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33044  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33045  * @cfg {String} iconTooltip default "This field is required"
33046  * @cfg {String} indicatorpos (left|right) default left
33047  * 
33048  * @constructor
33049  * Create a new FieldLabel
33050  * @param {Object} config The config object
33051  */
33052
33053 Roo.bootstrap.FieldLabel = function(config){
33054     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33055     
33056     this.addEvents({
33057             /**
33058              * @event invalid
33059              * Fires after the field has been marked as invalid.
33060              * @param {Roo.form.FieldLabel} this
33061              * @param {String} msg The validation message
33062              */
33063             invalid : true,
33064             /**
33065              * @event valid
33066              * Fires after the field has been validated with no errors.
33067              * @param {Roo.form.FieldLabel} this
33068              */
33069             valid : true
33070         });
33071 };
33072
33073 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33074     
33075     tag: 'label',
33076     cls: '',
33077     html: '',
33078     target: '',
33079     allowBlank : true,
33080     invalidClass : 'has-warning',
33081     validClass : 'has-success',
33082     iconTooltip : 'This field is required',
33083     indicatorpos : 'left',
33084     
33085     getAutoCreate : function(){
33086         
33087         var cls = "";
33088         if (!this.allowBlank) {
33089             cls  = "visible";
33090         }
33091         
33092         var cfg = {
33093             tag : this.tag,
33094             cls : 'roo-bootstrap-field-label ' + this.cls,
33095             for : this.target,
33096             cn : [
33097                 {
33098                     tag : 'i',
33099                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33100                     tooltip : this.iconTooltip
33101                 },
33102                 {
33103                     tag : 'span',
33104                     html : this.html
33105                 }
33106             ] 
33107         };
33108         
33109         if(this.indicatorpos == 'right'){
33110             var cfg = {
33111                 tag : this.tag,
33112                 cls : 'roo-bootstrap-field-label ' + this.cls,
33113                 for : this.target,
33114                 cn : [
33115                     {
33116                         tag : 'span',
33117                         html : this.html
33118                     },
33119                     {
33120                         tag : 'i',
33121                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33122                         tooltip : this.iconTooltip
33123                     }
33124                 ] 
33125             };
33126         }
33127         
33128         return cfg;
33129     },
33130     
33131     initEvents: function() 
33132     {
33133         Roo.bootstrap.Element.superclass.initEvents.call(this);
33134         
33135         this.indicator = this.indicatorEl();
33136         
33137         if(this.indicator){
33138             this.indicator.removeClass('visible');
33139             this.indicator.addClass('invisible');
33140         }
33141         
33142         Roo.bootstrap.FieldLabel.register(this);
33143     },
33144     
33145     indicatorEl : function()
33146     {
33147         var indicator = this.el.select('i.roo-required-indicator',true).first();
33148         
33149         if(!indicator){
33150             return false;
33151         }
33152         
33153         return indicator;
33154         
33155     },
33156     
33157     /**
33158      * Mark this field as valid
33159      */
33160     markValid : function()
33161     {
33162         if(this.indicator){
33163             this.indicator.removeClass('visible');
33164             this.indicator.addClass('invisible');
33165         }
33166         if (Roo.bootstrap.version == 3) {
33167             this.el.removeClass(this.invalidClass);
33168             this.el.addClass(this.validClass);
33169         } else {
33170             this.el.removeClass('is-invalid');
33171             this.el.addClass('is-valid');
33172         }
33173         
33174         
33175         this.fireEvent('valid', this);
33176     },
33177     
33178     /**
33179      * Mark this field as invalid
33180      * @param {String} msg The validation message
33181      */
33182     markInvalid : function(msg)
33183     {
33184         if(this.indicator){
33185             this.indicator.removeClass('invisible');
33186             this.indicator.addClass('visible');
33187         }
33188           if (Roo.bootstrap.version == 3) {
33189             this.el.removeClass(this.validClass);
33190             this.el.addClass(this.invalidClass);
33191         } else {
33192             this.el.removeClass('is-valid');
33193             this.el.addClass('is-invalid');
33194         }
33195         
33196         
33197         this.fireEvent('invalid', this, msg);
33198     }
33199     
33200    
33201 });
33202
33203 Roo.apply(Roo.bootstrap.FieldLabel, {
33204     
33205     groups: {},
33206     
33207      /**
33208     * register a FieldLabel Group
33209     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33210     */
33211     register : function(label)
33212     {
33213         if(this.groups.hasOwnProperty(label.target)){
33214             return;
33215         }
33216      
33217         this.groups[label.target] = label;
33218         
33219     },
33220     /**
33221     * fetch a FieldLabel Group based on the target
33222     * @param {string} target
33223     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33224     */
33225     get: function(target) {
33226         if (typeof(this.groups[target]) == 'undefined') {
33227             return false;
33228         }
33229         
33230         return this.groups[target] ;
33231     }
33232 });
33233
33234  
33235
33236  /*
33237  * - LGPL
33238  *
33239  * page DateSplitField.
33240  * 
33241  */
33242
33243
33244 /**
33245  * @class Roo.bootstrap.DateSplitField
33246  * @extends Roo.bootstrap.Component
33247  * Bootstrap DateSplitField class
33248  * @cfg {string} fieldLabel - the label associated
33249  * @cfg {Number} labelWidth set the width of label (0-12)
33250  * @cfg {String} labelAlign (top|left)
33251  * @cfg {Boolean} dayAllowBlank (true|false) default false
33252  * @cfg {Boolean} monthAllowBlank (true|false) default false
33253  * @cfg {Boolean} yearAllowBlank (true|false) default false
33254  * @cfg {string} dayPlaceholder 
33255  * @cfg {string} monthPlaceholder
33256  * @cfg {string} yearPlaceholder
33257  * @cfg {string} dayFormat default 'd'
33258  * @cfg {string} monthFormat default 'm'
33259  * @cfg {string} yearFormat default 'Y'
33260  * @cfg {Number} labellg set the width of label (1-12)
33261  * @cfg {Number} labelmd set the width of label (1-12)
33262  * @cfg {Number} labelsm set the width of label (1-12)
33263  * @cfg {Number} labelxs set the width of label (1-12)
33264
33265  *     
33266  * @constructor
33267  * Create a new DateSplitField
33268  * @param {Object} config The config object
33269  */
33270
33271 Roo.bootstrap.DateSplitField = function(config){
33272     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33273     
33274     this.addEvents({
33275         // raw events
33276          /**
33277          * @event years
33278          * getting the data of years
33279          * @param {Roo.bootstrap.DateSplitField} this
33280          * @param {Object} years
33281          */
33282         "years" : true,
33283         /**
33284          * @event days
33285          * getting the data of days
33286          * @param {Roo.bootstrap.DateSplitField} this
33287          * @param {Object} days
33288          */
33289         "days" : true,
33290         /**
33291          * @event invalid
33292          * Fires after the field has been marked as invalid.
33293          * @param {Roo.form.Field} this
33294          * @param {String} msg The validation message
33295          */
33296         invalid : true,
33297        /**
33298          * @event valid
33299          * Fires after the field has been validated with no errors.
33300          * @param {Roo.form.Field} this
33301          */
33302         valid : true
33303     });
33304 };
33305
33306 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33307     
33308     fieldLabel : '',
33309     labelAlign : 'top',
33310     labelWidth : 3,
33311     dayAllowBlank : false,
33312     monthAllowBlank : false,
33313     yearAllowBlank : false,
33314     dayPlaceholder : '',
33315     monthPlaceholder : '',
33316     yearPlaceholder : '',
33317     dayFormat : 'd',
33318     monthFormat : 'm',
33319     yearFormat : 'Y',
33320     isFormField : true,
33321     labellg : 0,
33322     labelmd : 0,
33323     labelsm : 0,
33324     labelxs : 0,
33325     
33326     getAutoCreate : function()
33327     {
33328         var cfg = {
33329             tag : 'div',
33330             cls : 'row roo-date-split-field-group',
33331             cn : [
33332                 {
33333                     tag : 'input',
33334                     type : 'hidden',
33335                     cls : 'form-hidden-field roo-date-split-field-group-value',
33336                     name : this.name
33337                 }
33338             ]
33339         };
33340         
33341         var labelCls = 'col-md-12';
33342         var contentCls = 'col-md-4';
33343         
33344         if(this.fieldLabel){
33345             
33346             var label = {
33347                 tag : 'div',
33348                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33349                 cn : [
33350                     {
33351                         tag : 'label',
33352                         html : this.fieldLabel
33353                     }
33354                 ]
33355             };
33356             
33357             if(this.labelAlign == 'left'){
33358             
33359                 if(this.labelWidth > 12){
33360                     label.style = "width: " + this.labelWidth + 'px';
33361                 }
33362
33363                 if(this.labelWidth < 13 && this.labelmd == 0){
33364                     this.labelmd = this.labelWidth;
33365                 }
33366
33367                 if(this.labellg > 0){
33368                     labelCls = ' col-lg-' + this.labellg;
33369                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33370                 }
33371
33372                 if(this.labelmd > 0){
33373                     labelCls = ' col-md-' + this.labelmd;
33374                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33375                 }
33376
33377                 if(this.labelsm > 0){
33378                     labelCls = ' col-sm-' + this.labelsm;
33379                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33380                 }
33381
33382                 if(this.labelxs > 0){
33383                     labelCls = ' col-xs-' + this.labelxs;
33384                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33385                 }
33386             }
33387             
33388             label.cls += ' ' + labelCls;
33389             
33390             cfg.cn.push(label);
33391         }
33392         
33393         Roo.each(['day', 'month', 'year'], function(t){
33394             cfg.cn.push({
33395                 tag : 'div',
33396                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33397             });
33398         }, this);
33399         
33400         return cfg;
33401     },
33402     
33403     inputEl: function ()
33404     {
33405         return this.el.select('.roo-date-split-field-group-value', true).first();
33406     },
33407     
33408     onRender : function(ct, position) 
33409     {
33410         var _this = this;
33411         
33412         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33413         
33414         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33415         
33416         this.dayField = new Roo.bootstrap.ComboBox({
33417             allowBlank : this.dayAllowBlank,
33418             alwaysQuery : true,
33419             displayField : 'value',
33420             editable : false,
33421             fieldLabel : '',
33422             forceSelection : true,
33423             mode : 'local',
33424             placeholder : this.dayPlaceholder,
33425             selectOnFocus : true,
33426             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33427             triggerAction : 'all',
33428             typeAhead : true,
33429             valueField : 'value',
33430             store : new Roo.data.SimpleStore({
33431                 data : (function() {    
33432                     var days = [];
33433                     _this.fireEvent('days', _this, days);
33434                     return days;
33435                 })(),
33436                 fields : [ 'value' ]
33437             }),
33438             listeners : {
33439                 select : function (_self, record, index)
33440                 {
33441                     _this.setValue(_this.getValue());
33442                 }
33443             }
33444         });
33445
33446         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33447         
33448         this.monthField = new Roo.bootstrap.MonthField({
33449             after : '<i class=\"fa fa-calendar\"></i>',
33450             allowBlank : this.monthAllowBlank,
33451             placeholder : this.monthPlaceholder,
33452             readOnly : true,
33453             listeners : {
33454                 render : function (_self)
33455                 {
33456                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33457                         e.preventDefault();
33458                         _self.focus();
33459                     });
33460                 },
33461                 select : function (_self, oldvalue, newvalue)
33462                 {
33463                     _this.setValue(_this.getValue());
33464                 }
33465             }
33466         });
33467         
33468         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33469         
33470         this.yearField = new Roo.bootstrap.ComboBox({
33471             allowBlank : this.yearAllowBlank,
33472             alwaysQuery : true,
33473             displayField : 'value',
33474             editable : false,
33475             fieldLabel : '',
33476             forceSelection : true,
33477             mode : 'local',
33478             placeholder : this.yearPlaceholder,
33479             selectOnFocus : true,
33480             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33481             triggerAction : 'all',
33482             typeAhead : true,
33483             valueField : 'value',
33484             store : new Roo.data.SimpleStore({
33485                 data : (function() {
33486                     var years = [];
33487                     _this.fireEvent('years', _this, years);
33488                     return years;
33489                 })(),
33490                 fields : [ 'value' ]
33491             }),
33492             listeners : {
33493                 select : function (_self, record, index)
33494                 {
33495                     _this.setValue(_this.getValue());
33496                 }
33497             }
33498         });
33499
33500         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33501     },
33502     
33503     setValue : function(v, format)
33504     {
33505         this.inputEl.dom.value = v;
33506         
33507         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33508         
33509         var d = Date.parseDate(v, f);
33510         
33511         if(!d){
33512             this.validate();
33513             return;
33514         }
33515         
33516         this.setDay(d.format(this.dayFormat));
33517         this.setMonth(d.format(this.monthFormat));
33518         this.setYear(d.format(this.yearFormat));
33519         
33520         this.validate();
33521         
33522         return;
33523     },
33524     
33525     setDay : function(v)
33526     {
33527         this.dayField.setValue(v);
33528         this.inputEl.dom.value = this.getValue();
33529         this.validate();
33530         return;
33531     },
33532     
33533     setMonth : function(v)
33534     {
33535         this.monthField.setValue(v, true);
33536         this.inputEl.dom.value = this.getValue();
33537         this.validate();
33538         return;
33539     },
33540     
33541     setYear : function(v)
33542     {
33543         this.yearField.setValue(v);
33544         this.inputEl.dom.value = this.getValue();
33545         this.validate();
33546         return;
33547     },
33548     
33549     getDay : function()
33550     {
33551         return this.dayField.getValue();
33552     },
33553     
33554     getMonth : function()
33555     {
33556         return this.monthField.getValue();
33557     },
33558     
33559     getYear : function()
33560     {
33561         return this.yearField.getValue();
33562     },
33563     
33564     getValue : function()
33565     {
33566         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33567         
33568         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33569         
33570         return date;
33571     },
33572     
33573     reset : function()
33574     {
33575         this.setDay('');
33576         this.setMonth('');
33577         this.setYear('');
33578         this.inputEl.dom.value = '';
33579         this.validate();
33580         return;
33581     },
33582     
33583     validate : function()
33584     {
33585         var d = this.dayField.validate();
33586         var m = this.monthField.validate();
33587         var y = this.yearField.validate();
33588         
33589         var valid = true;
33590         
33591         if(
33592                 (!this.dayAllowBlank && !d) ||
33593                 (!this.monthAllowBlank && !m) ||
33594                 (!this.yearAllowBlank && !y)
33595         ){
33596             valid = false;
33597         }
33598         
33599         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33600             return valid;
33601         }
33602         
33603         if(valid){
33604             this.markValid();
33605             return valid;
33606         }
33607         
33608         this.markInvalid();
33609         
33610         return valid;
33611     },
33612     
33613     markValid : function()
33614     {
33615         
33616         var label = this.el.select('label', true).first();
33617         var icon = this.el.select('i.fa-star', true).first();
33618
33619         if(label && icon){
33620             icon.remove();
33621         }
33622         
33623         this.fireEvent('valid', this);
33624     },
33625     
33626      /**
33627      * Mark this field as invalid
33628      * @param {String} msg The validation message
33629      */
33630     markInvalid : function(msg)
33631     {
33632         
33633         var label = this.el.select('label', true).first();
33634         var icon = this.el.select('i.fa-star', true).first();
33635
33636         if(label && !icon){
33637             this.el.select('.roo-date-split-field-label', true).createChild({
33638                 tag : 'i',
33639                 cls : 'text-danger fa fa-lg fa-star',
33640                 tooltip : 'This field is required',
33641                 style : 'margin-right:5px;'
33642             }, label, true);
33643         }
33644         
33645         this.fireEvent('invalid', this, msg);
33646     },
33647     
33648     clearInvalid : function()
33649     {
33650         var label = this.el.select('label', true).first();
33651         var icon = this.el.select('i.fa-star', true).first();
33652
33653         if(label && icon){
33654             icon.remove();
33655         }
33656         
33657         this.fireEvent('valid', this);
33658     },
33659     
33660     getName: function()
33661     {
33662         return this.name;
33663     }
33664     
33665 });
33666
33667  /**
33668  *
33669  * This is based on 
33670  * http://masonry.desandro.com
33671  *
33672  * The idea is to render all the bricks based on vertical width...
33673  *
33674  * The original code extends 'outlayer' - we might need to use that....
33675  * 
33676  */
33677
33678
33679 /**
33680  * @class Roo.bootstrap.LayoutMasonry
33681  * @extends Roo.bootstrap.Component
33682  * Bootstrap Layout Masonry class
33683  * 
33684  * @constructor
33685  * Create a new Element
33686  * @param {Object} config The config object
33687  */
33688
33689 Roo.bootstrap.LayoutMasonry = function(config){
33690     
33691     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33692     
33693     this.bricks = [];
33694     
33695     Roo.bootstrap.LayoutMasonry.register(this);
33696     
33697     this.addEvents({
33698         // raw events
33699         /**
33700          * @event layout
33701          * Fire after layout the items
33702          * @param {Roo.bootstrap.LayoutMasonry} this
33703          * @param {Roo.EventObject} e
33704          */
33705         "layout" : true
33706     });
33707     
33708 };
33709
33710 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33711     
33712     /**
33713      * @cfg {Boolean} isLayoutInstant = no animation?
33714      */   
33715     isLayoutInstant : false, // needed?
33716    
33717     /**
33718      * @cfg {Number} boxWidth  width of the columns
33719      */   
33720     boxWidth : 450,
33721     
33722       /**
33723      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33724      */   
33725     boxHeight : 0,
33726     
33727     /**
33728      * @cfg {Number} padWidth padding below box..
33729      */   
33730     padWidth : 10, 
33731     
33732     /**
33733      * @cfg {Number} gutter gutter width..
33734      */   
33735     gutter : 10,
33736     
33737      /**
33738      * @cfg {Number} maxCols maximum number of columns
33739      */   
33740     
33741     maxCols: 0,
33742     
33743     /**
33744      * @cfg {Boolean} isAutoInitial defalut true
33745      */   
33746     isAutoInitial : true, 
33747     
33748     containerWidth: 0,
33749     
33750     /**
33751      * @cfg {Boolean} isHorizontal defalut false
33752      */   
33753     isHorizontal : false, 
33754
33755     currentSize : null,
33756     
33757     tag: 'div',
33758     
33759     cls: '',
33760     
33761     bricks: null, //CompositeElement
33762     
33763     cols : 1,
33764     
33765     _isLayoutInited : false,
33766     
33767 //    isAlternative : false, // only use for vertical layout...
33768     
33769     /**
33770      * @cfg {Number} alternativePadWidth padding below box..
33771      */   
33772     alternativePadWidth : 50,
33773     
33774     selectedBrick : [],
33775     
33776     getAutoCreate : function(){
33777         
33778         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33779         
33780         var cfg = {
33781             tag: this.tag,
33782             cls: 'blog-masonary-wrapper ' + this.cls,
33783             cn : {
33784                 cls : 'mas-boxes masonary'
33785             }
33786         };
33787         
33788         return cfg;
33789     },
33790     
33791     getChildContainer: function( )
33792     {
33793         if (this.boxesEl) {
33794             return this.boxesEl;
33795         }
33796         
33797         this.boxesEl = this.el.select('.mas-boxes').first();
33798         
33799         return this.boxesEl;
33800     },
33801     
33802     
33803     initEvents : function()
33804     {
33805         var _this = this;
33806         
33807         if(this.isAutoInitial){
33808             Roo.log('hook children rendered');
33809             this.on('childrenrendered', function() {
33810                 Roo.log('children rendered');
33811                 _this.initial();
33812             } ,this);
33813         }
33814     },
33815     
33816     initial : function()
33817     {
33818         this.selectedBrick = [];
33819         
33820         this.currentSize = this.el.getBox(true);
33821         
33822         Roo.EventManager.onWindowResize(this.resize, this); 
33823
33824         if(!this.isAutoInitial){
33825             this.layout();
33826             return;
33827         }
33828         
33829         this.layout();
33830         
33831         return;
33832         //this.layout.defer(500,this);
33833         
33834     },
33835     
33836     resize : function()
33837     {
33838         var cs = this.el.getBox(true);
33839         
33840         if (
33841                 this.currentSize.width == cs.width && 
33842                 this.currentSize.x == cs.x && 
33843                 this.currentSize.height == cs.height && 
33844                 this.currentSize.y == cs.y 
33845         ) {
33846             Roo.log("no change in with or X or Y");
33847             return;
33848         }
33849         
33850         this.currentSize = cs;
33851         
33852         this.layout();
33853         
33854     },
33855     
33856     layout : function()
33857     {   
33858         this._resetLayout();
33859         
33860         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33861         
33862         this.layoutItems( isInstant );
33863       
33864         this._isLayoutInited = true;
33865         
33866         this.fireEvent('layout', this);
33867         
33868     },
33869     
33870     _resetLayout : function()
33871     {
33872         if(this.isHorizontal){
33873             this.horizontalMeasureColumns();
33874             return;
33875         }
33876         
33877         this.verticalMeasureColumns();
33878         
33879     },
33880     
33881     verticalMeasureColumns : function()
33882     {
33883         this.getContainerWidth();
33884         
33885 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33886 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33887 //            return;
33888 //        }
33889         
33890         var boxWidth = this.boxWidth + this.padWidth;
33891         
33892         if(this.containerWidth < this.boxWidth){
33893             boxWidth = this.containerWidth
33894         }
33895         
33896         var containerWidth = this.containerWidth;
33897         
33898         var cols = Math.floor(containerWidth / boxWidth);
33899         
33900         this.cols = Math.max( cols, 1 );
33901         
33902         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33903         
33904         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33905         
33906         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33907         
33908         this.colWidth = boxWidth + avail - this.padWidth;
33909         
33910         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33911         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33912     },
33913     
33914     horizontalMeasureColumns : function()
33915     {
33916         this.getContainerWidth();
33917         
33918         var boxWidth = this.boxWidth;
33919         
33920         if(this.containerWidth < boxWidth){
33921             boxWidth = this.containerWidth;
33922         }
33923         
33924         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33925         
33926         this.el.setHeight(boxWidth);
33927         
33928     },
33929     
33930     getContainerWidth : function()
33931     {
33932         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33933     },
33934     
33935     layoutItems : function( isInstant )
33936     {
33937         Roo.log(this.bricks);
33938         
33939         var items = Roo.apply([], this.bricks);
33940         
33941         if(this.isHorizontal){
33942             this._horizontalLayoutItems( items , isInstant );
33943             return;
33944         }
33945         
33946 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33947 //            this._verticalAlternativeLayoutItems( items , isInstant );
33948 //            return;
33949 //        }
33950         
33951         this._verticalLayoutItems( items , isInstant );
33952         
33953     },
33954     
33955     _verticalLayoutItems : function ( items , isInstant)
33956     {
33957         if ( !items || !items.length ) {
33958             return;
33959         }
33960         
33961         var standard = [
33962             ['xs', 'xs', 'xs', 'tall'],
33963             ['xs', 'xs', 'tall'],
33964             ['xs', 'xs', 'sm'],
33965             ['xs', 'xs', 'xs'],
33966             ['xs', 'tall'],
33967             ['xs', 'sm'],
33968             ['xs', 'xs'],
33969             ['xs'],
33970             
33971             ['sm', 'xs', 'xs'],
33972             ['sm', 'xs'],
33973             ['sm'],
33974             
33975             ['tall', 'xs', 'xs', 'xs'],
33976             ['tall', 'xs', 'xs'],
33977             ['tall', 'xs'],
33978             ['tall']
33979             
33980         ];
33981         
33982         var queue = [];
33983         
33984         var boxes = [];
33985         
33986         var box = [];
33987         
33988         Roo.each(items, function(item, k){
33989             
33990             switch (item.size) {
33991                 // these layouts take up a full box,
33992                 case 'md' :
33993                 case 'md-left' :
33994                 case 'md-right' :
33995                 case 'wide' :
33996                     
33997                     if(box.length){
33998                         boxes.push(box);
33999                         box = [];
34000                     }
34001                     
34002                     boxes.push([item]);
34003                     
34004                     break;
34005                     
34006                 case 'xs' :
34007                 case 'sm' :
34008                 case 'tall' :
34009                     
34010                     box.push(item);
34011                     
34012                     break;
34013                 default :
34014                     break;
34015                     
34016             }
34017             
34018         }, this);
34019         
34020         if(box.length){
34021             boxes.push(box);
34022             box = [];
34023         }
34024         
34025         var filterPattern = function(box, length)
34026         {
34027             if(!box.length){
34028                 return;
34029             }
34030             
34031             var match = false;
34032             
34033             var pattern = box.slice(0, length);
34034             
34035             var format = [];
34036             
34037             Roo.each(pattern, function(i){
34038                 format.push(i.size);
34039             }, this);
34040             
34041             Roo.each(standard, function(s){
34042                 
34043                 if(String(s) != String(format)){
34044                     return;
34045                 }
34046                 
34047                 match = true;
34048                 return false;
34049                 
34050             }, this);
34051             
34052             if(!match && length == 1){
34053                 return;
34054             }
34055             
34056             if(!match){
34057                 filterPattern(box, length - 1);
34058                 return;
34059             }
34060                 
34061             queue.push(pattern);
34062
34063             box = box.slice(length, box.length);
34064
34065             filterPattern(box, 4);
34066
34067             return;
34068             
34069         }
34070         
34071         Roo.each(boxes, function(box, k){
34072             
34073             if(!box.length){
34074                 return;
34075             }
34076             
34077             if(box.length == 1){
34078                 queue.push(box);
34079                 return;
34080             }
34081             
34082             filterPattern(box, 4);
34083             
34084         }, this);
34085         
34086         this._processVerticalLayoutQueue( queue, isInstant );
34087         
34088     },
34089     
34090 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34091 //    {
34092 //        if ( !items || !items.length ) {
34093 //            return;
34094 //        }
34095 //
34096 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34097 //        
34098 //    },
34099     
34100     _horizontalLayoutItems : function ( items , isInstant)
34101     {
34102         if ( !items || !items.length || items.length < 3) {
34103             return;
34104         }
34105         
34106         items.reverse();
34107         
34108         var eItems = items.slice(0, 3);
34109         
34110         items = items.slice(3, items.length);
34111         
34112         var standard = [
34113             ['xs', 'xs', 'xs', 'wide'],
34114             ['xs', 'xs', 'wide'],
34115             ['xs', 'xs', 'sm'],
34116             ['xs', 'xs', 'xs'],
34117             ['xs', 'wide'],
34118             ['xs', 'sm'],
34119             ['xs', 'xs'],
34120             ['xs'],
34121             
34122             ['sm', 'xs', 'xs'],
34123             ['sm', 'xs'],
34124             ['sm'],
34125             
34126             ['wide', 'xs', 'xs', 'xs'],
34127             ['wide', 'xs', 'xs'],
34128             ['wide', 'xs'],
34129             ['wide'],
34130             
34131             ['wide-thin']
34132         ];
34133         
34134         var queue = [];
34135         
34136         var boxes = [];
34137         
34138         var box = [];
34139         
34140         Roo.each(items, function(item, k){
34141             
34142             switch (item.size) {
34143                 case 'md' :
34144                 case 'md-left' :
34145                 case 'md-right' :
34146                 case 'tall' :
34147                     
34148                     if(box.length){
34149                         boxes.push(box);
34150                         box = [];
34151                     }
34152                     
34153                     boxes.push([item]);
34154                     
34155                     break;
34156                     
34157                 case 'xs' :
34158                 case 'sm' :
34159                 case 'wide' :
34160                 case 'wide-thin' :
34161                     
34162                     box.push(item);
34163                     
34164                     break;
34165                 default :
34166                     break;
34167                     
34168             }
34169             
34170         }, this);
34171         
34172         if(box.length){
34173             boxes.push(box);
34174             box = [];
34175         }
34176         
34177         var filterPattern = function(box, length)
34178         {
34179             if(!box.length){
34180                 return;
34181             }
34182             
34183             var match = false;
34184             
34185             var pattern = box.slice(0, length);
34186             
34187             var format = [];
34188             
34189             Roo.each(pattern, function(i){
34190                 format.push(i.size);
34191             }, this);
34192             
34193             Roo.each(standard, function(s){
34194                 
34195                 if(String(s) != String(format)){
34196                     return;
34197                 }
34198                 
34199                 match = true;
34200                 return false;
34201                 
34202             }, this);
34203             
34204             if(!match && length == 1){
34205                 return;
34206             }
34207             
34208             if(!match){
34209                 filterPattern(box, length - 1);
34210                 return;
34211             }
34212                 
34213             queue.push(pattern);
34214
34215             box = box.slice(length, box.length);
34216
34217             filterPattern(box, 4);
34218
34219             return;
34220             
34221         }
34222         
34223         Roo.each(boxes, function(box, k){
34224             
34225             if(!box.length){
34226                 return;
34227             }
34228             
34229             if(box.length == 1){
34230                 queue.push(box);
34231                 return;
34232             }
34233             
34234             filterPattern(box, 4);
34235             
34236         }, this);
34237         
34238         
34239         var prune = [];
34240         
34241         var pos = this.el.getBox(true);
34242         
34243         var minX = pos.x;
34244         
34245         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34246         
34247         var hit_end = false;
34248         
34249         Roo.each(queue, function(box){
34250             
34251             if(hit_end){
34252                 
34253                 Roo.each(box, function(b){
34254                 
34255                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34256                     b.el.hide();
34257
34258                 }, this);
34259
34260                 return;
34261             }
34262             
34263             var mx = 0;
34264             
34265             Roo.each(box, function(b){
34266                 
34267                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34268                 b.el.show();
34269
34270                 mx = Math.max(mx, b.x);
34271                 
34272             }, this);
34273             
34274             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34275             
34276             if(maxX < minX){
34277                 
34278                 Roo.each(box, function(b){
34279                 
34280                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34281                     b.el.hide();
34282                     
34283                 }, this);
34284                 
34285                 hit_end = true;
34286                 
34287                 return;
34288             }
34289             
34290             prune.push(box);
34291             
34292         }, this);
34293         
34294         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34295     },
34296     
34297     /** Sets position of item in DOM
34298     * @param {Element} item
34299     * @param {Number} x - horizontal position
34300     * @param {Number} y - vertical position
34301     * @param {Boolean} isInstant - disables transitions
34302     */
34303     _processVerticalLayoutQueue : function( queue, isInstant )
34304     {
34305         var pos = this.el.getBox(true);
34306         var x = pos.x;
34307         var y = pos.y;
34308         var maxY = [];
34309         
34310         for (var i = 0; i < this.cols; i++){
34311             maxY[i] = pos.y;
34312         }
34313         
34314         Roo.each(queue, function(box, k){
34315             
34316             var col = k % this.cols;
34317             
34318             Roo.each(box, function(b,kk){
34319                 
34320                 b.el.position('absolute');
34321                 
34322                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34323                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34324                 
34325                 if(b.size == 'md-left' || b.size == 'md-right'){
34326                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34327                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34328                 }
34329                 
34330                 b.el.setWidth(width);
34331                 b.el.setHeight(height);
34332                 // iframe?
34333                 b.el.select('iframe',true).setSize(width,height);
34334                 
34335             }, this);
34336             
34337             for (var i = 0; i < this.cols; i++){
34338                 
34339                 if(maxY[i] < maxY[col]){
34340                     col = i;
34341                     continue;
34342                 }
34343                 
34344                 col = Math.min(col, i);
34345                 
34346             }
34347             
34348             x = pos.x + col * (this.colWidth + this.padWidth);
34349             
34350             y = maxY[col];
34351             
34352             var positions = [];
34353             
34354             switch (box.length){
34355                 case 1 :
34356                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34357                     break;
34358                 case 2 :
34359                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34360                     break;
34361                 case 3 :
34362                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34363                     break;
34364                 case 4 :
34365                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34366                     break;
34367                 default :
34368                     break;
34369             }
34370             
34371             Roo.each(box, function(b,kk){
34372                 
34373                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34374                 
34375                 var sz = b.el.getSize();
34376                 
34377                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34378                 
34379             }, this);
34380             
34381         }, this);
34382         
34383         var mY = 0;
34384         
34385         for (var i = 0; i < this.cols; i++){
34386             mY = Math.max(mY, maxY[i]);
34387         }
34388         
34389         this.el.setHeight(mY - pos.y);
34390         
34391     },
34392     
34393 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34394 //    {
34395 //        var pos = this.el.getBox(true);
34396 //        var x = pos.x;
34397 //        var y = pos.y;
34398 //        var maxX = pos.right;
34399 //        
34400 //        var maxHeight = 0;
34401 //        
34402 //        Roo.each(items, function(item, k){
34403 //            
34404 //            var c = k % 2;
34405 //            
34406 //            item.el.position('absolute');
34407 //                
34408 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34409 //
34410 //            item.el.setWidth(width);
34411 //
34412 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34413 //
34414 //            item.el.setHeight(height);
34415 //            
34416 //            if(c == 0){
34417 //                item.el.setXY([x, y], isInstant ? false : true);
34418 //            } else {
34419 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34420 //            }
34421 //            
34422 //            y = y + height + this.alternativePadWidth;
34423 //            
34424 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34425 //            
34426 //        }, this);
34427 //        
34428 //        this.el.setHeight(maxHeight);
34429 //        
34430 //    },
34431     
34432     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34433     {
34434         var pos = this.el.getBox(true);
34435         
34436         var minX = pos.x;
34437         var minY = pos.y;
34438         
34439         var maxX = pos.right;
34440         
34441         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34442         
34443         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34444         
34445         Roo.each(queue, function(box, k){
34446             
34447             Roo.each(box, function(b, kk){
34448                 
34449                 b.el.position('absolute');
34450                 
34451                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34452                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34453                 
34454                 if(b.size == 'md-left' || b.size == 'md-right'){
34455                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34456                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34457                 }
34458                 
34459                 b.el.setWidth(width);
34460                 b.el.setHeight(height);
34461                 
34462             }, this);
34463             
34464             if(!box.length){
34465                 return;
34466             }
34467             
34468             var positions = [];
34469             
34470             switch (box.length){
34471                 case 1 :
34472                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34473                     break;
34474                 case 2 :
34475                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34476                     break;
34477                 case 3 :
34478                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34479                     break;
34480                 case 4 :
34481                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34482                     break;
34483                 default :
34484                     break;
34485             }
34486             
34487             Roo.each(box, function(b,kk){
34488                 
34489                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34490                 
34491                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34492                 
34493             }, this);
34494             
34495         }, this);
34496         
34497     },
34498     
34499     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34500     {
34501         Roo.each(eItems, function(b,k){
34502             
34503             b.size = (k == 0) ? 'sm' : 'xs';
34504             b.x = (k == 0) ? 2 : 1;
34505             b.y = (k == 0) ? 2 : 1;
34506             
34507             b.el.position('absolute');
34508             
34509             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34510                 
34511             b.el.setWidth(width);
34512             
34513             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34514             
34515             b.el.setHeight(height);
34516             
34517         }, this);
34518
34519         var positions = [];
34520         
34521         positions.push({
34522             x : maxX - this.unitWidth * 2 - this.gutter,
34523             y : minY
34524         });
34525         
34526         positions.push({
34527             x : maxX - this.unitWidth,
34528             y : minY + (this.unitWidth + this.gutter) * 2
34529         });
34530         
34531         positions.push({
34532             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34533             y : minY
34534         });
34535         
34536         Roo.each(eItems, function(b,k){
34537             
34538             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34539
34540         }, this);
34541         
34542     },
34543     
34544     getVerticalOneBoxColPositions : function(x, y, box)
34545     {
34546         var pos = [];
34547         
34548         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34549         
34550         if(box[0].size == 'md-left'){
34551             rand = 0;
34552         }
34553         
34554         if(box[0].size == 'md-right'){
34555             rand = 1;
34556         }
34557         
34558         pos.push({
34559             x : x + (this.unitWidth + this.gutter) * rand,
34560             y : y
34561         });
34562         
34563         return pos;
34564     },
34565     
34566     getVerticalTwoBoxColPositions : function(x, y, box)
34567     {
34568         var pos = [];
34569         
34570         if(box[0].size == 'xs'){
34571             
34572             pos.push({
34573                 x : x,
34574                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34575             });
34576
34577             pos.push({
34578                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34579                 y : y
34580             });
34581             
34582             return pos;
34583             
34584         }
34585         
34586         pos.push({
34587             x : x,
34588             y : y
34589         });
34590
34591         pos.push({
34592             x : x + (this.unitWidth + this.gutter) * 2,
34593             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34594         });
34595         
34596         return pos;
34597         
34598     },
34599     
34600     getVerticalThreeBoxColPositions : function(x, y, box)
34601     {
34602         var pos = [];
34603         
34604         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34605             
34606             pos.push({
34607                 x : x,
34608                 y : y
34609             });
34610
34611             pos.push({
34612                 x : x + (this.unitWidth + this.gutter) * 1,
34613                 y : y
34614             });
34615             
34616             pos.push({
34617                 x : x + (this.unitWidth + this.gutter) * 2,
34618                 y : y
34619             });
34620             
34621             return pos;
34622             
34623         }
34624         
34625         if(box[0].size == 'xs' && box[1].size == 'xs'){
34626             
34627             pos.push({
34628                 x : x,
34629                 y : y
34630             });
34631
34632             pos.push({
34633                 x : x,
34634                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34635             });
34636             
34637             pos.push({
34638                 x : x + (this.unitWidth + this.gutter) * 1,
34639                 y : y
34640             });
34641             
34642             return pos;
34643             
34644         }
34645         
34646         pos.push({
34647             x : x,
34648             y : y
34649         });
34650
34651         pos.push({
34652             x : x + (this.unitWidth + this.gutter) * 2,
34653             y : y
34654         });
34655
34656         pos.push({
34657             x : x + (this.unitWidth + this.gutter) * 2,
34658             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34659         });
34660             
34661         return pos;
34662         
34663     },
34664     
34665     getVerticalFourBoxColPositions : function(x, y, box)
34666     {
34667         var pos = [];
34668         
34669         if(box[0].size == 'xs'){
34670             
34671             pos.push({
34672                 x : x,
34673                 y : y
34674             });
34675
34676             pos.push({
34677                 x : x,
34678                 y : y + (this.unitHeight + this.gutter) * 1
34679             });
34680             
34681             pos.push({
34682                 x : x,
34683                 y : y + (this.unitHeight + this.gutter) * 2
34684             });
34685             
34686             pos.push({
34687                 x : x + (this.unitWidth + this.gutter) * 1,
34688                 y : y
34689             });
34690             
34691             return pos;
34692             
34693         }
34694         
34695         pos.push({
34696             x : x,
34697             y : y
34698         });
34699
34700         pos.push({
34701             x : x + (this.unitWidth + this.gutter) * 2,
34702             y : y
34703         });
34704
34705         pos.push({
34706             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34707             y : y + (this.unitHeight + this.gutter) * 1
34708         });
34709
34710         pos.push({
34711             x : x + (this.unitWidth + this.gutter) * 2,
34712             y : y + (this.unitWidth + this.gutter) * 2
34713         });
34714
34715         return pos;
34716         
34717     },
34718     
34719     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34720     {
34721         var pos = [];
34722         
34723         if(box[0].size == 'md-left'){
34724             pos.push({
34725                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34726                 y : minY
34727             });
34728             
34729             return pos;
34730         }
34731         
34732         if(box[0].size == 'md-right'){
34733             pos.push({
34734                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34735                 y : minY + (this.unitWidth + this.gutter) * 1
34736             });
34737             
34738             return pos;
34739         }
34740         
34741         var rand = Math.floor(Math.random() * (4 - box[0].y));
34742         
34743         pos.push({
34744             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34745             y : minY + (this.unitWidth + this.gutter) * rand
34746         });
34747         
34748         return pos;
34749         
34750     },
34751     
34752     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34753     {
34754         var pos = [];
34755         
34756         if(box[0].size == 'xs'){
34757             
34758             pos.push({
34759                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34760                 y : minY
34761             });
34762
34763             pos.push({
34764                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34765                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34766             });
34767             
34768             return pos;
34769             
34770         }
34771         
34772         pos.push({
34773             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34774             y : minY
34775         });
34776
34777         pos.push({
34778             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34779             y : minY + (this.unitWidth + this.gutter) * 2
34780         });
34781         
34782         return pos;
34783         
34784     },
34785     
34786     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34787     {
34788         var pos = [];
34789         
34790         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34791             
34792             pos.push({
34793                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34794                 y : minY
34795             });
34796
34797             pos.push({
34798                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34799                 y : minY + (this.unitWidth + this.gutter) * 1
34800             });
34801             
34802             pos.push({
34803                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34804                 y : minY + (this.unitWidth + this.gutter) * 2
34805             });
34806             
34807             return pos;
34808             
34809         }
34810         
34811         if(box[0].size == 'xs' && box[1].size == 'xs'){
34812             
34813             pos.push({
34814                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34815                 y : minY
34816             });
34817
34818             pos.push({
34819                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34820                 y : minY
34821             });
34822             
34823             pos.push({
34824                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34825                 y : minY + (this.unitWidth + this.gutter) * 1
34826             });
34827             
34828             return pos;
34829             
34830         }
34831         
34832         pos.push({
34833             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34834             y : minY
34835         });
34836
34837         pos.push({
34838             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34839             y : minY + (this.unitWidth + this.gutter) * 2
34840         });
34841
34842         pos.push({
34843             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34844             y : minY + (this.unitWidth + this.gutter) * 2
34845         });
34846             
34847         return pos;
34848         
34849     },
34850     
34851     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34852     {
34853         var pos = [];
34854         
34855         if(box[0].size == 'xs'){
34856             
34857             pos.push({
34858                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34859                 y : minY
34860             });
34861
34862             pos.push({
34863                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34864                 y : minY
34865             });
34866             
34867             pos.push({
34868                 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),
34869                 y : minY
34870             });
34871             
34872             pos.push({
34873                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34874                 y : minY + (this.unitWidth + this.gutter) * 1
34875             });
34876             
34877             return pos;
34878             
34879         }
34880         
34881         pos.push({
34882             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34883             y : minY
34884         });
34885         
34886         pos.push({
34887             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34888             y : minY + (this.unitWidth + this.gutter) * 2
34889         });
34890         
34891         pos.push({
34892             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34893             y : minY + (this.unitWidth + this.gutter) * 2
34894         });
34895         
34896         pos.push({
34897             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),
34898             y : minY + (this.unitWidth + this.gutter) * 2
34899         });
34900
34901         return pos;
34902         
34903     },
34904     
34905     /**
34906     * remove a Masonry Brick
34907     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34908     */
34909     removeBrick : function(brick_id)
34910     {
34911         if (!brick_id) {
34912             return;
34913         }
34914         
34915         for (var i = 0; i<this.bricks.length; i++) {
34916             if (this.bricks[i].id == brick_id) {
34917                 this.bricks.splice(i,1);
34918                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34919                 this.initial();
34920             }
34921         }
34922     },
34923     
34924     /**
34925     * adds a Masonry Brick
34926     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34927     */
34928     addBrick : function(cfg)
34929     {
34930         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34931         //this.register(cn);
34932         cn.parentId = this.id;
34933         cn.render(this.el);
34934         return cn;
34935     },
34936     
34937     /**
34938     * register a Masonry Brick
34939     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34940     */
34941     
34942     register : function(brick)
34943     {
34944         this.bricks.push(brick);
34945         brick.masonryId = this.id;
34946     },
34947     
34948     /**
34949     * clear all the Masonry Brick
34950     */
34951     clearAll : function()
34952     {
34953         this.bricks = [];
34954         //this.getChildContainer().dom.innerHTML = "";
34955         this.el.dom.innerHTML = '';
34956     },
34957     
34958     getSelected : function()
34959     {
34960         if (!this.selectedBrick) {
34961             return false;
34962         }
34963         
34964         return this.selectedBrick;
34965     }
34966 });
34967
34968 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34969     
34970     groups: {},
34971      /**
34972     * register a Masonry Layout
34973     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34974     */
34975     
34976     register : function(layout)
34977     {
34978         this.groups[layout.id] = layout;
34979     },
34980     /**
34981     * fetch a  Masonry Layout based on the masonry layout ID
34982     * @param {string} the masonry layout to add
34983     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34984     */
34985     
34986     get: function(layout_id) {
34987         if (typeof(this.groups[layout_id]) == 'undefined') {
34988             return false;
34989         }
34990         return this.groups[layout_id] ;
34991     }
34992     
34993     
34994     
34995 });
34996
34997  
34998
34999  /**
35000  *
35001  * This is based on 
35002  * http://masonry.desandro.com
35003  *
35004  * The idea is to render all the bricks based on vertical width...
35005  *
35006  * The original code extends 'outlayer' - we might need to use that....
35007  * 
35008  */
35009
35010
35011 /**
35012  * @class Roo.bootstrap.LayoutMasonryAuto
35013  * @extends Roo.bootstrap.Component
35014  * Bootstrap Layout Masonry class
35015  * 
35016  * @constructor
35017  * Create a new Element
35018  * @param {Object} config The config object
35019  */
35020
35021 Roo.bootstrap.LayoutMasonryAuto = function(config){
35022     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35023 };
35024
35025 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35026     
35027       /**
35028      * @cfg {Boolean} isFitWidth  - resize the width..
35029      */   
35030     isFitWidth : false,  // options..
35031     /**
35032      * @cfg {Boolean} isOriginLeft = left align?
35033      */   
35034     isOriginLeft : true,
35035     /**
35036      * @cfg {Boolean} isOriginTop = top align?
35037      */   
35038     isOriginTop : false,
35039     /**
35040      * @cfg {Boolean} isLayoutInstant = no animation?
35041      */   
35042     isLayoutInstant : false, // needed?
35043     /**
35044      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35045      */   
35046     isResizingContainer : true,
35047     /**
35048      * @cfg {Number} columnWidth  width of the columns 
35049      */   
35050     
35051     columnWidth : 0,
35052     
35053     /**
35054      * @cfg {Number} maxCols maximum number of columns
35055      */   
35056     
35057     maxCols: 0,
35058     /**
35059      * @cfg {Number} padHeight padding below box..
35060      */   
35061     
35062     padHeight : 10, 
35063     
35064     /**
35065      * @cfg {Boolean} isAutoInitial defalut true
35066      */   
35067     
35068     isAutoInitial : true, 
35069     
35070     // private?
35071     gutter : 0,
35072     
35073     containerWidth: 0,
35074     initialColumnWidth : 0,
35075     currentSize : null,
35076     
35077     colYs : null, // array.
35078     maxY : 0,
35079     padWidth: 10,
35080     
35081     
35082     tag: 'div',
35083     cls: '',
35084     bricks: null, //CompositeElement
35085     cols : 0, // array?
35086     // element : null, // wrapped now this.el
35087     _isLayoutInited : null, 
35088     
35089     
35090     getAutoCreate : function(){
35091         
35092         var cfg = {
35093             tag: this.tag,
35094             cls: 'blog-masonary-wrapper ' + this.cls,
35095             cn : {
35096                 cls : 'mas-boxes masonary'
35097             }
35098         };
35099         
35100         return cfg;
35101     },
35102     
35103     getChildContainer: function( )
35104     {
35105         if (this.boxesEl) {
35106             return this.boxesEl;
35107         }
35108         
35109         this.boxesEl = this.el.select('.mas-boxes').first();
35110         
35111         return this.boxesEl;
35112     },
35113     
35114     
35115     initEvents : function()
35116     {
35117         var _this = this;
35118         
35119         if(this.isAutoInitial){
35120             Roo.log('hook children rendered');
35121             this.on('childrenrendered', function() {
35122                 Roo.log('children rendered');
35123                 _this.initial();
35124             } ,this);
35125         }
35126         
35127     },
35128     
35129     initial : function()
35130     {
35131         this.reloadItems();
35132
35133         this.currentSize = this.el.getBox(true);
35134
35135         /// was window resize... - let's see if this works..
35136         Roo.EventManager.onWindowResize(this.resize, this); 
35137
35138         if(!this.isAutoInitial){
35139             this.layout();
35140             return;
35141         }
35142         
35143         this.layout.defer(500,this);
35144     },
35145     
35146     reloadItems: function()
35147     {
35148         this.bricks = this.el.select('.masonry-brick', true);
35149         
35150         this.bricks.each(function(b) {
35151             //Roo.log(b.getSize());
35152             if (!b.attr('originalwidth')) {
35153                 b.attr('originalwidth',  b.getSize().width);
35154             }
35155             
35156         });
35157         
35158         Roo.log(this.bricks.elements.length);
35159     },
35160     
35161     resize : function()
35162     {
35163         Roo.log('resize');
35164         var cs = this.el.getBox(true);
35165         
35166         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35167             Roo.log("no change in with or X");
35168             return;
35169         }
35170         this.currentSize = cs;
35171         this.layout();
35172     },
35173     
35174     layout : function()
35175     {
35176          Roo.log('layout');
35177         this._resetLayout();
35178         //this._manageStamps();
35179       
35180         // don't animate first layout
35181         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35182         this.layoutItems( isInstant );
35183       
35184         // flag for initalized
35185         this._isLayoutInited = true;
35186     },
35187     
35188     layoutItems : function( isInstant )
35189     {
35190         //var items = this._getItemsForLayout( this.items );
35191         // original code supports filtering layout items.. we just ignore it..
35192         
35193         this._layoutItems( this.bricks , isInstant );
35194       
35195         this._postLayout();
35196     },
35197     _layoutItems : function ( items , isInstant)
35198     {
35199        //this.fireEvent( 'layout', this, items );
35200     
35201
35202         if ( !items || !items.elements.length ) {
35203           // no items, emit event with empty array
35204             return;
35205         }
35206
35207         var queue = [];
35208         items.each(function(item) {
35209             Roo.log("layout item");
35210             Roo.log(item);
35211             // get x/y object from method
35212             var position = this._getItemLayoutPosition( item );
35213             // enqueue
35214             position.item = item;
35215             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35216             queue.push( position );
35217         }, this);
35218       
35219         this._processLayoutQueue( queue );
35220     },
35221     /** Sets position of item in DOM
35222     * @param {Element} item
35223     * @param {Number} x - horizontal position
35224     * @param {Number} y - vertical position
35225     * @param {Boolean} isInstant - disables transitions
35226     */
35227     _processLayoutQueue : function( queue )
35228     {
35229         for ( var i=0, len = queue.length; i < len; i++ ) {
35230             var obj = queue[i];
35231             obj.item.position('absolute');
35232             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35233         }
35234     },
35235       
35236     
35237     /**
35238     * Any logic you want to do after each layout,
35239     * i.e. size the container
35240     */
35241     _postLayout : function()
35242     {
35243         this.resizeContainer();
35244     },
35245     
35246     resizeContainer : function()
35247     {
35248         if ( !this.isResizingContainer ) {
35249             return;
35250         }
35251         var size = this._getContainerSize();
35252         if ( size ) {
35253             this.el.setSize(size.width,size.height);
35254             this.boxesEl.setSize(size.width,size.height);
35255         }
35256     },
35257     
35258     
35259     
35260     _resetLayout : function()
35261     {
35262         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35263         this.colWidth = this.el.getWidth();
35264         //this.gutter = this.el.getWidth(); 
35265         
35266         this.measureColumns();
35267
35268         // reset column Y
35269         var i = this.cols;
35270         this.colYs = [];
35271         while (i--) {
35272             this.colYs.push( 0 );
35273         }
35274     
35275         this.maxY = 0;
35276     },
35277
35278     measureColumns : function()
35279     {
35280         this.getContainerWidth();
35281       // if columnWidth is 0, default to outerWidth of first item
35282         if ( !this.columnWidth ) {
35283             var firstItem = this.bricks.first();
35284             Roo.log(firstItem);
35285             this.columnWidth  = this.containerWidth;
35286             if (firstItem && firstItem.attr('originalwidth') ) {
35287                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35288             }
35289             // columnWidth fall back to item of first element
35290             Roo.log("set column width?");
35291                         this.initialColumnWidth = this.columnWidth  ;
35292
35293             // if first elem has no width, default to size of container
35294             
35295         }
35296         
35297         
35298         if (this.initialColumnWidth) {
35299             this.columnWidth = this.initialColumnWidth;
35300         }
35301         
35302         
35303             
35304         // column width is fixed at the top - however if container width get's smaller we should
35305         // reduce it...
35306         
35307         // this bit calcs how man columns..
35308             
35309         var columnWidth = this.columnWidth += this.gutter;
35310       
35311         // calculate columns
35312         var containerWidth = this.containerWidth + this.gutter;
35313         
35314         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35315         // fix rounding errors, typically with gutters
35316         var excess = columnWidth - containerWidth % columnWidth;
35317         
35318         
35319         // if overshoot is less than a pixel, round up, otherwise floor it
35320         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35321         cols = Math[ mathMethod ]( cols );
35322         this.cols = Math.max( cols, 1 );
35323         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35324         
35325          // padding positioning..
35326         var totalColWidth = this.cols * this.columnWidth;
35327         var padavail = this.containerWidth - totalColWidth;
35328         // so for 2 columns - we need 3 'pads'
35329         
35330         var padNeeded = (1+this.cols) * this.padWidth;
35331         
35332         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35333         
35334         this.columnWidth += padExtra
35335         //this.padWidth = Math.floor(padavail /  ( this.cols));
35336         
35337         // adjust colum width so that padding is fixed??
35338         
35339         // we have 3 columns ... total = width * 3
35340         // we have X left over... that should be used by 
35341         
35342         //if (this.expandC) {
35343             
35344         //}
35345         
35346         
35347         
35348     },
35349     
35350     getContainerWidth : function()
35351     {
35352        /* // container is parent if fit width
35353         var container = this.isFitWidth ? this.element.parentNode : this.element;
35354         // check that this.size and size are there
35355         // IE8 triggers resize on body size change, so they might not be
35356         
35357         var size = getSize( container );  //FIXME
35358         this.containerWidth = size && size.innerWidth; //FIXME
35359         */
35360          
35361         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35362         
35363     },
35364     
35365     _getItemLayoutPosition : function( item )  // what is item?
35366     {
35367         // we resize the item to our columnWidth..
35368       
35369         item.setWidth(this.columnWidth);
35370         item.autoBoxAdjust  = false;
35371         
35372         var sz = item.getSize();
35373  
35374         // how many columns does this brick span
35375         var remainder = this.containerWidth % this.columnWidth;
35376         
35377         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35378         // round if off by 1 pixel, otherwise use ceil
35379         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35380         colSpan = Math.min( colSpan, this.cols );
35381         
35382         // normally this should be '1' as we dont' currently allow multi width columns..
35383         
35384         var colGroup = this._getColGroup( colSpan );
35385         // get the minimum Y value from the columns
35386         var minimumY = Math.min.apply( Math, colGroup );
35387         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35388         
35389         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35390          
35391         // position the brick
35392         var position = {
35393             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35394             y: this.currentSize.y + minimumY + this.padHeight
35395         };
35396         
35397         Roo.log(position);
35398         // apply setHeight to necessary columns
35399         var setHeight = minimumY + sz.height + this.padHeight;
35400         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35401         
35402         var setSpan = this.cols + 1 - colGroup.length;
35403         for ( var i = 0; i < setSpan; i++ ) {
35404           this.colYs[ shortColIndex + i ] = setHeight ;
35405         }
35406       
35407         return position;
35408     },
35409     
35410     /**
35411      * @param {Number} colSpan - number of columns the element spans
35412      * @returns {Array} colGroup
35413      */
35414     _getColGroup : function( colSpan )
35415     {
35416         if ( colSpan < 2 ) {
35417           // if brick spans only one column, use all the column Ys
35418           return this.colYs;
35419         }
35420       
35421         var colGroup = [];
35422         // how many different places could this brick fit horizontally
35423         var groupCount = this.cols + 1 - colSpan;
35424         // for each group potential horizontal position
35425         for ( var i = 0; i < groupCount; i++ ) {
35426           // make an array of colY values for that one group
35427           var groupColYs = this.colYs.slice( i, i + colSpan );
35428           // and get the max value of the array
35429           colGroup[i] = Math.max.apply( Math, groupColYs );
35430         }
35431         return colGroup;
35432     },
35433     /*
35434     _manageStamp : function( stamp )
35435     {
35436         var stampSize =  stamp.getSize();
35437         var offset = stamp.getBox();
35438         // get the columns that this stamp affects
35439         var firstX = this.isOriginLeft ? offset.x : offset.right;
35440         var lastX = firstX + stampSize.width;
35441         var firstCol = Math.floor( firstX / this.columnWidth );
35442         firstCol = Math.max( 0, firstCol );
35443         
35444         var lastCol = Math.floor( lastX / this.columnWidth );
35445         // lastCol should not go over if multiple of columnWidth #425
35446         lastCol -= lastX % this.columnWidth ? 0 : 1;
35447         lastCol = Math.min( this.cols - 1, lastCol );
35448         
35449         // set colYs to bottom of the stamp
35450         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35451             stampSize.height;
35452             
35453         for ( var i = firstCol; i <= lastCol; i++ ) {
35454           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35455         }
35456     },
35457     */
35458     
35459     _getContainerSize : function()
35460     {
35461         this.maxY = Math.max.apply( Math, this.colYs );
35462         var size = {
35463             height: this.maxY
35464         };
35465       
35466         if ( this.isFitWidth ) {
35467             size.width = this._getContainerFitWidth();
35468         }
35469       
35470         return size;
35471     },
35472     
35473     _getContainerFitWidth : function()
35474     {
35475         var unusedCols = 0;
35476         // count unused columns
35477         var i = this.cols;
35478         while ( --i ) {
35479           if ( this.colYs[i] !== 0 ) {
35480             break;
35481           }
35482           unusedCols++;
35483         }
35484         // fit container to columns that have been used
35485         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35486     },
35487     
35488     needsResizeLayout : function()
35489     {
35490         var previousWidth = this.containerWidth;
35491         this.getContainerWidth();
35492         return previousWidth !== this.containerWidth;
35493     }
35494  
35495 });
35496
35497  
35498
35499  /*
35500  * - LGPL
35501  *
35502  * element
35503  * 
35504  */
35505
35506 /**
35507  * @class Roo.bootstrap.MasonryBrick
35508  * @extends Roo.bootstrap.Component
35509  * Bootstrap MasonryBrick class
35510  * 
35511  * @constructor
35512  * Create a new MasonryBrick
35513  * @param {Object} config The config object
35514  */
35515
35516 Roo.bootstrap.MasonryBrick = function(config){
35517     
35518     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35519     
35520     Roo.bootstrap.MasonryBrick.register(this);
35521     
35522     this.addEvents({
35523         // raw events
35524         /**
35525          * @event click
35526          * When a MasonryBrick is clcik
35527          * @param {Roo.bootstrap.MasonryBrick} this
35528          * @param {Roo.EventObject} e
35529          */
35530         "click" : true
35531     });
35532 };
35533
35534 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35535     
35536     /**
35537      * @cfg {String} title
35538      */   
35539     title : '',
35540     /**
35541      * @cfg {String} html
35542      */   
35543     html : '',
35544     /**
35545      * @cfg {String} bgimage
35546      */   
35547     bgimage : '',
35548     /**
35549      * @cfg {String} videourl
35550      */   
35551     videourl : '',
35552     /**
35553      * @cfg {String} cls
35554      */   
35555     cls : '',
35556     /**
35557      * @cfg {String} href
35558      */   
35559     href : '',
35560     /**
35561      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35562      */   
35563     size : 'xs',
35564     
35565     /**
35566      * @cfg {String} placetitle (center|bottom)
35567      */   
35568     placetitle : '',
35569     
35570     /**
35571      * @cfg {Boolean} isFitContainer defalut true
35572      */   
35573     isFitContainer : true, 
35574     
35575     /**
35576      * @cfg {Boolean} preventDefault defalut false
35577      */   
35578     preventDefault : false, 
35579     
35580     /**
35581      * @cfg {Boolean} inverse defalut false
35582      */   
35583     maskInverse : false, 
35584     
35585     getAutoCreate : function()
35586     {
35587         if(!this.isFitContainer){
35588             return this.getSplitAutoCreate();
35589         }
35590         
35591         var cls = 'masonry-brick masonry-brick-full';
35592         
35593         if(this.href.length){
35594             cls += ' masonry-brick-link';
35595         }
35596         
35597         if(this.bgimage.length){
35598             cls += ' masonry-brick-image';
35599         }
35600         
35601         if(this.maskInverse){
35602             cls += ' mask-inverse';
35603         }
35604         
35605         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35606             cls += ' enable-mask';
35607         }
35608         
35609         if(this.size){
35610             cls += ' masonry-' + this.size + '-brick';
35611         }
35612         
35613         if(this.placetitle.length){
35614             
35615             switch (this.placetitle) {
35616                 case 'center' :
35617                     cls += ' masonry-center-title';
35618                     break;
35619                 case 'bottom' :
35620                     cls += ' masonry-bottom-title';
35621                     break;
35622                 default:
35623                     break;
35624             }
35625             
35626         } else {
35627             if(!this.html.length && !this.bgimage.length){
35628                 cls += ' masonry-center-title';
35629             }
35630
35631             if(!this.html.length && this.bgimage.length){
35632                 cls += ' masonry-bottom-title';
35633             }
35634         }
35635         
35636         if(this.cls){
35637             cls += ' ' + this.cls;
35638         }
35639         
35640         var cfg = {
35641             tag: (this.href.length) ? 'a' : 'div',
35642             cls: cls,
35643             cn: [
35644                 {
35645                     tag: 'div',
35646                     cls: 'masonry-brick-mask'
35647                 },
35648                 {
35649                     tag: 'div',
35650                     cls: 'masonry-brick-paragraph',
35651                     cn: []
35652                 }
35653             ]
35654         };
35655         
35656         if(this.href.length){
35657             cfg.href = this.href;
35658         }
35659         
35660         var cn = cfg.cn[1].cn;
35661         
35662         if(this.title.length){
35663             cn.push({
35664                 tag: 'h4',
35665                 cls: 'masonry-brick-title',
35666                 html: this.title
35667             });
35668         }
35669         
35670         if(this.html.length){
35671             cn.push({
35672                 tag: 'p',
35673                 cls: 'masonry-brick-text',
35674                 html: this.html
35675             });
35676         }
35677         
35678         if (!this.title.length && !this.html.length) {
35679             cfg.cn[1].cls += ' hide';
35680         }
35681         
35682         if(this.bgimage.length){
35683             cfg.cn.push({
35684                 tag: 'img',
35685                 cls: 'masonry-brick-image-view',
35686                 src: this.bgimage
35687             });
35688         }
35689         
35690         if(this.videourl.length){
35691             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35692             // youtube support only?
35693             cfg.cn.push({
35694                 tag: 'iframe',
35695                 cls: 'masonry-brick-image-view',
35696                 src: vurl,
35697                 frameborder : 0,
35698                 allowfullscreen : true
35699             });
35700         }
35701         
35702         return cfg;
35703         
35704     },
35705     
35706     getSplitAutoCreate : function()
35707     {
35708         var cls = 'masonry-brick masonry-brick-split';
35709         
35710         if(this.href.length){
35711             cls += ' masonry-brick-link';
35712         }
35713         
35714         if(this.bgimage.length){
35715             cls += ' masonry-brick-image';
35716         }
35717         
35718         if(this.size){
35719             cls += ' masonry-' + this.size + '-brick';
35720         }
35721         
35722         switch (this.placetitle) {
35723             case 'center' :
35724                 cls += ' masonry-center-title';
35725                 break;
35726             case 'bottom' :
35727                 cls += ' masonry-bottom-title';
35728                 break;
35729             default:
35730                 if(!this.bgimage.length){
35731                     cls += ' masonry-center-title';
35732                 }
35733
35734                 if(this.bgimage.length){
35735                     cls += ' masonry-bottom-title';
35736                 }
35737                 break;
35738         }
35739         
35740         if(this.cls){
35741             cls += ' ' + this.cls;
35742         }
35743         
35744         var cfg = {
35745             tag: (this.href.length) ? 'a' : 'div',
35746             cls: cls,
35747             cn: [
35748                 {
35749                     tag: 'div',
35750                     cls: 'masonry-brick-split-head',
35751                     cn: [
35752                         {
35753                             tag: 'div',
35754                             cls: 'masonry-brick-paragraph',
35755                             cn: []
35756                         }
35757                     ]
35758                 },
35759                 {
35760                     tag: 'div',
35761                     cls: 'masonry-brick-split-body',
35762                     cn: []
35763                 }
35764             ]
35765         };
35766         
35767         if(this.href.length){
35768             cfg.href = this.href;
35769         }
35770         
35771         if(this.title.length){
35772             cfg.cn[0].cn[0].cn.push({
35773                 tag: 'h4',
35774                 cls: 'masonry-brick-title',
35775                 html: this.title
35776             });
35777         }
35778         
35779         if(this.html.length){
35780             cfg.cn[1].cn.push({
35781                 tag: 'p',
35782                 cls: 'masonry-brick-text',
35783                 html: this.html
35784             });
35785         }
35786
35787         if(this.bgimage.length){
35788             cfg.cn[0].cn.push({
35789                 tag: 'img',
35790                 cls: 'masonry-brick-image-view',
35791                 src: this.bgimage
35792             });
35793         }
35794         
35795         if(this.videourl.length){
35796             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35797             // youtube support only?
35798             cfg.cn[0].cn.cn.push({
35799                 tag: 'iframe',
35800                 cls: 'masonry-brick-image-view',
35801                 src: vurl,
35802                 frameborder : 0,
35803                 allowfullscreen : true
35804             });
35805         }
35806         
35807         return cfg;
35808     },
35809     
35810     initEvents: function() 
35811     {
35812         switch (this.size) {
35813             case 'xs' :
35814                 this.x = 1;
35815                 this.y = 1;
35816                 break;
35817             case 'sm' :
35818                 this.x = 2;
35819                 this.y = 2;
35820                 break;
35821             case 'md' :
35822             case 'md-left' :
35823             case 'md-right' :
35824                 this.x = 3;
35825                 this.y = 3;
35826                 break;
35827             case 'tall' :
35828                 this.x = 2;
35829                 this.y = 3;
35830                 break;
35831             case 'wide' :
35832                 this.x = 3;
35833                 this.y = 2;
35834                 break;
35835             case 'wide-thin' :
35836                 this.x = 3;
35837                 this.y = 1;
35838                 break;
35839                         
35840             default :
35841                 break;
35842         }
35843         
35844         if(Roo.isTouch){
35845             this.el.on('touchstart', this.onTouchStart, this);
35846             this.el.on('touchmove', this.onTouchMove, this);
35847             this.el.on('touchend', this.onTouchEnd, this);
35848             this.el.on('contextmenu', this.onContextMenu, this);
35849         } else {
35850             this.el.on('mouseenter'  ,this.enter, this);
35851             this.el.on('mouseleave', this.leave, this);
35852             this.el.on('click', this.onClick, this);
35853         }
35854         
35855         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35856             this.parent().bricks.push(this);   
35857         }
35858         
35859     },
35860     
35861     onClick: function(e, el)
35862     {
35863         var time = this.endTimer - this.startTimer;
35864         // Roo.log(e.preventDefault());
35865         if(Roo.isTouch){
35866             if(time > 1000){
35867                 e.preventDefault();
35868                 return;
35869             }
35870         }
35871         
35872         if(!this.preventDefault){
35873             return;
35874         }
35875         
35876         e.preventDefault();
35877         
35878         if (this.activeClass != '') {
35879             this.selectBrick();
35880         }
35881         
35882         this.fireEvent('click', this, e);
35883     },
35884     
35885     enter: function(e, el)
35886     {
35887         e.preventDefault();
35888         
35889         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35890             return;
35891         }
35892         
35893         if(this.bgimage.length && this.html.length){
35894             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35895         }
35896     },
35897     
35898     leave: function(e, el)
35899     {
35900         e.preventDefault();
35901         
35902         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35903             return;
35904         }
35905         
35906         if(this.bgimage.length && this.html.length){
35907             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35908         }
35909     },
35910     
35911     onTouchStart: function(e, el)
35912     {
35913 //        e.preventDefault();
35914         
35915         this.touchmoved = false;
35916         
35917         if(!this.isFitContainer){
35918             return;
35919         }
35920         
35921         if(!this.bgimage.length || !this.html.length){
35922             return;
35923         }
35924         
35925         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35926         
35927         this.timer = new Date().getTime();
35928         
35929     },
35930     
35931     onTouchMove: function(e, el)
35932     {
35933         this.touchmoved = true;
35934     },
35935     
35936     onContextMenu : function(e,el)
35937     {
35938         e.preventDefault();
35939         e.stopPropagation();
35940         return false;
35941     },
35942     
35943     onTouchEnd: function(e, el)
35944     {
35945 //        e.preventDefault();
35946         
35947         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35948         
35949             this.leave(e,el);
35950             
35951             return;
35952         }
35953         
35954         if(!this.bgimage.length || !this.html.length){
35955             
35956             if(this.href.length){
35957                 window.location.href = this.href;
35958             }
35959             
35960             return;
35961         }
35962         
35963         if(!this.isFitContainer){
35964             return;
35965         }
35966         
35967         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35968         
35969         window.location.href = this.href;
35970     },
35971     
35972     //selection on single brick only
35973     selectBrick : function() {
35974         
35975         if (!this.parentId) {
35976             return;
35977         }
35978         
35979         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35980         var index = m.selectedBrick.indexOf(this.id);
35981         
35982         if ( index > -1) {
35983             m.selectedBrick.splice(index,1);
35984             this.el.removeClass(this.activeClass);
35985             return;
35986         }
35987         
35988         for(var i = 0; i < m.selectedBrick.length; i++) {
35989             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35990             b.el.removeClass(b.activeClass);
35991         }
35992         
35993         m.selectedBrick = [];
35994         
35995         m.selectedBrick.push(this.id);
35996         this.el.addClass(this.activeClass);
35997         return;
35998     },
35999     
36000     isSelected : function(){
36001         return this.el.hasClass(this.activeClass);
36002         
36003     }
36004 });
36005
36006 Roo.apply(Roo.bootstrap.MasonryBrick, {
36007     
36008     //groups: {},
36009     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36010      /**
36011     * register a Masonry Brick
36012     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36013     */
36014     
36015     register : function(brick)
36016     {
36017         //this.groups[brick.id] = brick;
36018         this.groups.add(brick.id, brick);
36019     },
36020     /**
36021     * fetch a  masonry brick based on the masonry brick ID
36022     * @param {string} the masonry brick to add
36023     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36024     */
36025     
36026     get: function(brick_id) 
36027     {
36028         // if (typeof(this.groups[brick_id]) == 'undefined') {
36029         //     return false;
36030         // }
36031         // return this.groups[brick_id] ;
36032         
36033         if(this.groups.key(brick_id)) {
36034             return this.groups.key(brick_id);
36035         }
36036         
36037         return false;
36038     }
36039     
36040     
36041     
36042 });
36043
36044  /*
36045  * - LGPL
36046  *
36047  * element
36048  * 
36049  */
36050
36051 /**
36052  * @class Roo.bootstrap.Brick
36053  * @extends Roo.bootstrap.Component
36054  * Bootstrap Brick class
36055  * 
36056  * @constructor
36057  * Create a new Brick
36058  * @param {Object} config The config object
36059  */
36060
36061 Roo.bootstrap.Brick = function(config){
36062     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36063     
36064     this.addEvents({
36065         // raw events
36066         /**
36067          * @event click
36068          * When a Brick is click
36069          * @param {Roo.bootstrap.Brick} this
36070          * @param {Roo.EventObject} e
36071          */
36072         "click" : true
36073     });
36074 };
36075
36076 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36077     
36078     /**
36079      * @cfg {String} title
36080      */   
36081     title : '',
36082     /**
36083      * @cfg {String} html
36084      */   
36085     html : '',
36086     /**
36087      * @cfg {String} bgimage
36088      */   
36089     bgimage : '',
36090     /**
36091      * @cfg {String} cls
36092      */   
36093     cls : '',
36094     /**
36095      * @cfg {String} href
36096      */   
36097     href : '',
36098     /**
36099      * @cfg {String} video
36100      */   
36101     video : '',
36102     /**
36103      * @cfg {Boolean} square
36104      */   
36105     square : true,
36106     
36107     getAutoCreate : function()
36108     {
36109         var cls = 'roo-brick';
36110         
36111         if(this.href.length){
36112             cls += ' roo-brick-link';
36113         }
36114         
36115         if(this.bgimage.length){
36116             cls += ' roo-brick-image';
36117         }
36118         
36119         if(!this.html.length && !this.bgimage.length){
36120             cls += ' roo-brick-center-title';
36121         }
36122         
36123         if(!this.html.length && this.bgimage.length){
36124             cls += ' roo-brick-bottom-title';
36125         }
36126         
36127         if(this.cls){
36128             cls += ' ' + this.cls;
36129         }
36130         
36131         var cfg = {
36132             tag: (this.href.length) ? 'a' : 'div',
36133             cls: cls,
36134             cn: [
36135                 {
36136                     tag: 'div',
36137                     cls: 'roo-brick-paragraph',
36138                     cn: []
36139                 }
36140             ]
36141         };
36142         
36143         if(this.href.length){
36144             cfg.href = this.href;
36145         }
36146         
36147         var cn = cfg.cn[0].cn;
36148         
36149         if(this.title.length){
36150             cn.push({
36151                 tag: 'h4',
36152                 cls: 'roo-brick-title',
36153                 html: this.title
36154             });
36155         }
36156         
36157         if(this.html.length){
36158             cn.push({
36159                 tag: 'p',
36160                 cls: 'roo-brick-text',
36161                 html: this.html
36162             });
36163         } else {
36164             cn.cls += ' hide';
36165         }
36166         
36167         if(this.bgimage.length){
36168             cfg.cn.push({
36169                 tag: 'img',
36170                 cls: 'roo-brick-image-view',
36171                 src: this.bgimage
36172             });
36173         }
36174         
36175         return cfg;
36176     },
36177     
36178     initEvents: function() 
36179     {
36180         if(this.title.length || this.html.length){
36181             this.el.on('mouseenter'  ,this.enter, this);
36182             this.el.on('mouseleave', this.leave, this);
36183         }
36184         
36185         Roo.EventManager.onWindowResize(this.resize, this); 
36186         
36187         if(this.bgimage.length){
36188             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36189             this.imageEl.on('load', this.onImageLoad, this);
36190             return;
36191         }
36192         
36193         this.resize();
36194     },
36195     
36196     onImageLoad : function()
36197     {
36198         this.resize();
36199     },
36200     
36201     resize : function()
36202     {
36203         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36204         
36205         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36206         
36207         if(this.bgimage.length){
36208             var image = this.el.select('.roo-brick-image-view', true).first();
36209             
36210             image.setWidth(paragraph.getWidth());
36211             
36212             if(this.square){
36213                 image.setHeight(paragraph.getWidth());
36214             }
36215             
36216             this.el.setHeight(image.getHeight());
36217             paragraph.setHeight(image.getHeight());
36218             
36219         }
36220         
36221     },
36222     
36223     enter: function(e, el)
36224     {
36225         e.preventDefault();
36226         
36227         if(this.bgimage.length){
36228             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36229             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36230         }
36231     },
36232     
36233     leave: function(e, el)
36234     {
36235         e.preventDefault();
36236         
36237         if(this.bgimage.length){
36238             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36239             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36240         }
36241     }
36242     
36243 });
36244
36245  
36246
36247  /*
36248  * - LGPL
36249  *
36250  * Number field 
36251  */
36252
36253 /**
36254  * @class Roo.bootstrap.NumberField
36255  * @extends Roo.bootstrap.Input
36256  * Bootstrap NumberField class
36257  * 
36258  * 
36259  * 
36260  * 
36261  * @constructor
36262  * Create a new NumberField
36263  * @param {Object} config The config object
36264  */
36265
36266 Roo.bootstrap.NumberField = function(config){
36267     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36268 };
36269
36270 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36271     
36272     /**
36273      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36274      */
36275     allowDecimals : true,
36276     /**
36277      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36278      */
36279     decimalSeparator : ".",
36280     /**
36281      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36282      */
36283     decimalPrecision : 2,
36284     /**
36285      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36286      */
36287     allowNegative : true,
36288     
36289     /**
36290      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36291      */
36292     allowZero: true,
36293     /**
36294      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36295      */
36296     minValue : Number.NEGATIVE_INFINITY,
36297     /**
36298      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36299      */
36300     maxValue : Number.MAX_VALUE,
36301     /**
36302      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36303      */
36304     minText : "The minimum value for this field is {0}",
36305     /**
36306      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36307      */
36308     maxText : "The maximum value for this field is {0}",
36309     /**
36310      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36311      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36312      */
36313     nanText : "{0} is not a valid number",
36314     /**
36315      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36316      */
36317     thousandsDelimiter : false,
36318     /**
36319      * @cfg {String} valueAlign alignment of value
36320      */
36321     valueAlign : "left",
36322
36323     getAutoCreate : function()
36324     {
36325         var hiddenInput = {
36326             tag: 'input',
36327             type: 'hidden',
36328             id: Roo.id(),
36329             cls: 'hidden-number-input'
36330         };
36331         
36332         if (this.name) {
36333             hiddenInput.name = this.name;
36334         }
36335         
36336         this.name = '';
36337         
36338         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36339         
36340         this.name = hiddenInput.name;
36341         
36342         if(cfg.cn.length > 0) {
36343             cfg.cn.push(hiddenInput);
36344         }
36345         
36346         return cfg;
36347     },
36348
36349     // private
36350     initEvents : function()
36351     {   
36352         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36353         
36354         var allowed = "0123456789";
36355         
36356         if(this.allowDecimals){
36357             allowed += this.decimalSeparator;
36358         }
36359         
36360         if(this.allowNegative){
36361             allowed += "-";
36362         }
36363         
36364         if(this.thousandsDelimiter) {
36365             allowed += ",";
36366         }
36367         
36368         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36369         
36370         var keyPress = function(e){
36371             
36372             var k = e.getKey();
36373             
36374             var c = e.getCharCode();
36375             
36376             if(
36377                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36378                     allowed.indexOf(String.fromCharCode(c)) === -1
36379             ){
36380                 e.stopEvent();
36381                 return;
36382             }
36383             
36384             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36385                 return;
36386             }
36387             
36388             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36389                 e.stopEvent();
36390             }
36391         };
36392         
36393         this.el.on("keypress", keyPress, this);
36394     },
36395     
36396     validateValue : function(value)
36397     {
36398         
36399         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36400             return false;
36401         }
36402         
36403         var num = this.parseValue(value);
36404         
36405         if(isNaN(num)){
36406             this.markInvalid(String.format(this.nanText, value));
36407             return false;
36408         }
36409         
36410         if(num < this.minValue){
36411             this.markInvalid(String.format(this.minText, this.minValue));
36412             return false;
36413         }
36414         
36415         if(num > this.maxValue){
36416             this.markInvalid(String.format(this.maxText, this.maxValue));
36417             return false;
36418         }
36419         
36420         return true;
36421     },
36422
36423     getValue : function()
36424     {
36425         var v = this.hiddenEl().getValue();
36426         
36427         return this.fixPrecision(this.parseValue(v));
36428     },
36429
36430     parseValue : function(value)
36431     {
36432         if(this.thousandsDelimiter) {
36433             value += "";
36434             r = new RegExp(",", "g");
36435             value = value.replace(r, "");
36436         }
36437         
36438         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36439         return isNaN(value) ? '' : value;
36440     },
36441
36442     fixPrecision : function(value)
36443     {
36444         if(this.thousandsDelimiter) {
36445             value += "";
36446             r = new RegExp(",", "g");
36447             value = value.replace(r, "");
36448         }
36449         
36450         var nan = isNaN(value);
36451         
36452         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36453             return nan ? '' : value;
36454         }
36455         return parseFloat(value).toFixed(this.decimalPrecision);
36456     },
36457
36458     setValue : function(v)
36459     {
36460         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36461         
36462         this.value = v;
36463         
36464         if(this.rendered){
36465             
36466             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36467             
36468             this.inputEl().dom.value = (v == '') ? '' :
36469                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36470             
36471             if(!this.allowZero && v === '0') {
36472                 this.hiddenEl().dom.value = '';
36473                 this.inputEl().dom.value = '';
36474             }
36475             
36476             this.validate();
36477         }
36478     },
36479
36480     decimalPrecisionFcn : function(v)
36481     {
36482         return Math.floor(v);
36483     },
36484
36485     beforeBlur : function()
36486     {
36487         var v = this.parseValue(this.getRawValue());
36488         
36489         if(v || v === 0 || v === ''){
36490             this.setValue(v);
36491         }
36492     },
36493     
36494     hiddenEl : function()
36495     {
36496         return this.el.select('input.hidden-number-input',true).first();
36497     }
36498     
36499 });
36500
36501  
36502
36503 /*
36504 * Licence: LGPL
36505 */
36506
36507 /**
36508  * @class Roo.bootstrap.DocumentSlider
36509  * @extends Roo.bootstrap.Component
36510  * Bootstrap DocumentSlider class
36511  * 
36512  * @constructor
36513  * Create a new DocumentViewer
36514  * @param {Object} config The config object
36515  */
36516
36517 Roo.bootstrap.DocumentSlider = function(config){
36518     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36519     
36520     this.files = [];
36521     
36522     this.addEvents({
36523         /**
36524          * @event initial
36525          * Fire after initEvent
36526          * @param {Roo.bootstrap.DocumentSlider} this
36527          */
36528         "initial" : true,
36529         /**
36530          * @event update
36531          * Fire after update
36532          * @param {Roo.bootstrap.DocumentSlider} this
36533          */
36534         "update" : true,
36535         /**
36536          * @event click
36537          * Fire after click
36538          * @param {Roo.bootstrap.DocumentSlider} this
36539          */
36540         "click" : true
36541     });
36542 };
36543
36544 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36545     
36546     files : false,
36547     
36548     indicator : 0,
36549     
36550     getAutoCreate : function()
36551     {
36552         var cfg = {
36553             tag : 'div',
36554             cls : 'roo-document-slider',
36555             cn : [
36556                 {
36557                     tag : 'div',
36558                     cls : 'roo-document-slider-header',
36559                     cn : [
36560                         {
36561                             tag : 'div',
36562                             cls : 'roo-document-slider-header-title'
36563                         }
36564                     ]
36565                 },
36566                 {
36567                     tag : 'div',
36568                     cls : 'roo-document-slider-body',
36569                     cn : [
36570                         {
36571                             tag : 'div',
36572                             cls : 'roo-document-slider-prev',
36573                             cn : [
36574                                 {
36575                                     tag : 'i',
36576                                     cls : 'fa fa-chevron-left'
36577                                 }
36578                             ]
36579                         },
36580                         {
36581                             tag : 'div',
36582                             cls : 'roo-document-slider-thumb',
36583                             cn : [
36584                                 {
36585                                     tag : 'img',
36586                                     cls : 'roo-document-slider-image'
36587                                 }
36588                             ]
36589                         },
36590                         {
36591                             tag : 'div',
36592                             cls : 'roo-document-slider-next',
36593                             cn : [
36594                                 {
36595                                     tag : 'i',
36596                                     cls : 'fa fa-chevron-right'
36597                                 }
36598                             ]
36599                         }
36600                     ]
36601                 }
36602             ]
36603         };
36604         
36605         return cfg;
36606     },
36607     
36608     initEvents : function()
36609     {
36610         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36611         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36612         
36613         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36614         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36615         
36616         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36617         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36618         
36619         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36620         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36621         
36622         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36623         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36624         
36625         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36626         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36627         
36628         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36629         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36630         
36631         this.thumbEl.on('click', this.onClick, this);
36632         
36633         this.prevIndicator.on('click', this.prev, this);
36634         
36635         this.nextIndicator.on('click', this.next, this);
36636         
36637     },
36638     
36639     initial : function()
36640     {
36641         if(this.files.length){
36642             this.indicator = 1;
36643             this.update()
36644         }
36645         
36646         this.fireEvent('initial', this);
36647     },
36648     
36649     update : function()
36650     {
36651         this.imageEl.attr('src', this.files[this.indicator - 1]);
36652         
36653         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36654         
36655         this.prevIndicator.show();
36656         
36657         if(this.indicator == 1){
36658             this.prevIndicator.hide();
36659         }
36660         
36661         this.nextIndicator.show();
36662         
36663         if(this.indicator == this.files.length){
36664             this.nextIndicator.hide();
36665         }
36666         
36667         this.thumbEl.scrollTo('top');
36668         
36669         this.fireEvent('update', this);
36670     },
36671     
36672     onClick : function(e)
36673     {
36674         e.preventDefault();
36675         
36676         this.fireEvent('click', this);
36677     },
36678     
36679     prev : function(e)
36680     {
36681         e.preventDefault();
36682         
36683         this.indicator = Math.max(1, this.indicator - 1);
36684         
36685         this.update();
36686     },
36687     
36688     next : function(e)
36689     {
36690         e.preventDefault();
36691         
36692         this.indicator = Math.min(this.files.length, this.indicator + 1);
36693         
36694         this.update();
36695     }
36696 });
36697 /*
36698  * - LGPL
36699  *
36700  * RadioSet
36701  *
36702  *
36703  */
36704
36705 /**
36706  * @class Roo.bootstrap.RadioSet
36707  * @extends Roo.bootstrap.Input
36708  * Bootstrap RadioSet class
36709  * @cfg {String} indicatorpos (left|right) default left
36710  * @cfg {Boolean} inline (true|false) inline the element (default true)
36711  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36712  * @constructor
36713  * Create a new RadioSet
36714  * @param {Object} config The config object
36715  */
36716
36717 Roo.bootstrap.RadioSet = function(config){
36718     
36719     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36720     
36721     this.radioes = [];
36722     
36723     Roo.bootstrap.RadioSet.register(this);
36724     
36725     this.addEvents({
36726         /**
36727         * @event check
36728         * Fires when the element is checked or unchecked.
36729         * @param {Roo.bootstrap.RadioSet} this This radio
36730         * @param {Roo.bootstrap.Radio} item The checked item
36731         */
36732        check : true,
36733        /**
36734         * @event click
36735         * Fires when the element is click.
36736         * @param {Roo.bootstrap.RadioSet} this This radio set
36737         * @param {Roo.bootstrap.Radio} item The checked item
36738         * @param {Roo.EventObject} e The event object
36739         */
36740        click : true
36741     });
36742     
36743 };
36744
36745 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36746
36747     radioes : false,
36748     
36749     inline : true,
36750     
36751     weight : '',
36752     
36753     indicatorpos : 'left',
36754     
36755     getAutoCreate : function()
36756     {
36757         var label = {
36758             tag : 'label',
36759             cls : 'roo-radio-set-label',
36760             cn : [
36761                 {
36762                     tag : 'span',
36763                     html : this.fieldLabel
36764                 }
36765             ]
36766         };
36767         if (Roo.bootstrap.version == 3) {
36768             
36769             
36770             if(this.indicatorpos == 'left'){
36771                 label.cn.unshift({
36772                     tag : 'i',
36773                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36774                     tooltip : 'This field is required'
36775                 });
36776             } else {
36777                 label.cn.push({
36778                     tag : 'i',
36779                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36780                     tooltip : 'This field is required'
36781                 });
36782             }
36783         }
36784         var items = {
36785             tag : 'div',
36786             cls : 'roo-radio-set-items'
36787         };
36788         
36789         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36790         
36791         if (align === 'left' && this.fieldLabel.length) {
36792             
36793             items = {
36794                 cls : "roo-radio-set-right", 
36795                 cn: [
36796                     items
36797                 ]
36798             };
36799             
36800             if(this.labelWidth > 12){
36801                 label.style = "width: " + this.labelWidth + 'px';
36802             }
36803             
36804             if(this.labelWidth < 13 && this.labelmd == 0){
36805                 this.labelmd = this.labelWidth;
36806             }
36807             
36808             if(this.labellg > 0){
36809                 label.cls += ' col-lg-' + this.labellg;
36810                 items.cls += ' col-lg-' + (12 - this.labellg);
36811             }
36812             
36813             if(this.labelmd > 0){
36814                 label.cls += ' col-md-' + this.labelmd;
36815                 items.cls += ' col-md-' + (12 - this.labelmd);
36816             }
36817             
36818             if(this.labelsm > 0){
36819                 label.cls += ' col-sm-' + this.labelsm;
36820                 items.cls += ' col-sm-' + (12 - this.labelsm);
36821             }
36822             
36823             if(this.labelxs > 0){
36824                 label.cls += ' col-xs-' + this.labelxs;
36825                 items.cls += ' col-xs-' + (12 - this.labelxs);
36826             }
36827         }
36828         
36829         var cfg = {
36830             tag : 'div',
36831             cls : 'roo-radio-set',
36832             cn : [
36833                 {
36834                     tag : 'input',
36835                     cls : 'roo-radio-set-input',
36836                     type : 'hidden',
36837                     name : this.name,
36838                     value : this.value ? this.value :  ''
36839                 },
36840                 label,
36841                 items
36842             ]
36843         };
36844         
36845         if(this.weight.length){
36846             cfg.cls += ' roo-radio-' + this.weight;
36847         }
36848         
36849         if(this.inline) {
36850             cfg.cls += ' roo-radio-set-inline';
36851         }
36852         
36853         var settings=this;
36854         ['xs','sm','md','lg'].map(function(size){
36855             if (settings[size]) {
36856                 cfg.cls += ' col-' + size + '-' + settings[size];
36857             }
36858         });
36859         
36860         return cfg;
36861         
36862     },
36863
36864     initEvents : function()
36865     {
36866         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36867         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36868         
36869         if(!this.fieldLabel.length){
36870             this.labelEl.hide();
36871         }
36872         
36873         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36874         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36875         
36876         this.indicator = this.indicatorEl();
36877         
36878         if(this.indicator){
36879             this.indicator.addClass('invisible');
36880         }
36881         
36882         this.originalValue = this.getValue();
36883         
36884     },
36885     
36886     inputEl: function ()
36887     {
36888         return this.el.select('.roo-radio-set-input', true).first();
36889     },
36890     
36891     getChildContainer : function()
36892     {
36893         return this.itemsEl;
36894     },
36895     
36896     register : function(item)
36897     {
36898         this.radioes.push(item);
36899         
36900     },
36901     
36902     validate : function()
36903     {   
36904         if(this.getVisibilityEl().hasClass('hidden')){
36905             return true;
36906         }
36907         
36908         var valid = false;
36909         
36910         Roo.each(this.radioes, function(i){
36911             if(!i.checked){
36912                 return;
36913             }
36914             
36915             valid = true;
36916             return false;
36917         });
36918         
36919         if(this.allowBlank) {
36920             return true;
36921         }
36922         
36923         if(this.disabled || valid){
36924             this.markValid();
36925             return true;
36926         }
36927         
36928         this.markInvalid();
36929         return false;
36930         
36931     },
36932     
36933     markValid : function()
36934     {
36935         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36936             this.indicatorEl().removeClass('visible');
36937             this.indicatorEl().addClass('invisible');
36938         }
36939         
36940         
36941         if (Roo.bootstrap.version == 3) {
36942             this.el.removeClass([this.invalidClass, this.validClass]);
36943             this.el.addClass(this.validClass);
36944         } else {
36945             this.el.removeClass(['is-invalid','is-valid']);
36946             this.el.addClass(['is-valid']);
36947         }
36948         this.fireEvent('valid', this);
36949     },
36950     
36951     markInvalid : function(msg)
36952     {
36953         if(this.allowBlank || this.disabled){
36954             return;
36955         }
36956         
36957         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36958             this.indicatorEl().removeClass('invisible');
36959             this.indicatorEl().addClass('visible');
36960         }
36961         if (Roo.bootstrap.version == 3) {
36962             this.el.removeClass([this.invalidClass, this.validClass]);
36963             this.el.addClass(this.invalidClass);
36964         } else {
36965             this.el.removeClass(['is-invalid','is-valid']);
36966             this.el.addClass(['is-invalid']);
36967         }
36968         
36969         this.fireEvent('invalid', this, msg);
36970         
36971     },
36972     
36973     setValue : function(v, suppressEvent)
36974     {   
36975         if(this.value === v){
36976             return;
36977         }
36978         
36979         this.value = v;
36980         
36981         if(this.rendered){
36982             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36983         }
36984         
36985         Roo.each(this.radioes, function(i){
36986             i.checked = false;
36987             i.el.removeClass('checked');
36988         });
36989         
36990         Roo.each(this.radioes, function(i){
36991             
36992             if(i.value === v || i.value.toString() === v.toString()){
36993                 i.checked = true;
36994                 i.el.addClass('checked');
36995                 
36996                 if(suppressEvent !== true){
36997                     this.fireEvent('check', this, i);
36998                 }
36999                 
37000                 return false;
37001             }
37002             
37003         }, this);
37004         
37005         this.validate();
37006     },
37007     
37008     clearInvalid : function(){
37009         
37010         if(!this.el || this.preventMark){
37011             return;
37012         }
37013         
37014         this.el.removeClass([this.invalidClass]);
37015         
37016         this.fireEvent('valid', this);
37017     }
37018     
37019 });
37020
37021 Roo.apply(Roo.bootstrap.RadioSet, {
37022     
37023     groups: {},
37024     
37025     register : function(set)
37026     {
37027         this.groups[set.name] = set;
37028     },
37029     
37030     get: function(name) 
37031     {
37032         if (typeof(this.groups[name]) == 'undefined') {
37033             return false;
37034         }
37035         
37036         return this.groups[name] ;
37037     }
37038     
37039 });
37040 /*
37041  * Based on:
37042  * Ext JS Library 1.1.1
37043  * Copyright(c) 2006-2007, Ext JS, LLC.
37044  *
37045  * Originally Released Under LGPL - original licence link has changed is not relivant.
37046  *
37047  * Fork - LGPL
37048  * <script type="text/javascript">
37049  */
37050
37051
37052 /**
37053  * @class Roo.bootstrap.SplitBar
37054  * @extends Roo.util.Observable
37055  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37056  * <br><br>
37057  * Usage:
37058  * <pre><code>
37059 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37060                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37061 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37062 split.minSize = 100;
37063 split.maxSize = 600;
37064 split.animate = true;
37065 split.on('moved', splitterMoved);
37066 </code></pre>
37067  * @constructor
37068  * Create a new SplitBar
37069  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37070  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37071  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37072  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37073                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37074                         position of the SplitBar).
37075  */
37076 Roo.bootstrap.SplitBar = function(cfg){
37077     
37078     /** @private */
37079     
37080     //{
37081     //  dragElement : elm
37082     //  resizingElement: el,
37083         // optional..
37084     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37085     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37086         // existingProxy ???
37087     //}
37088     
37089     this.el = Roo.get(cfg.dragElement, true);
37090     this.el.dom.unselectable = "on";
37091     /** @private */
37092     this.resizingEl = Roo.get(cfg.resizingElement, true);
37093
37094     /**
37095      * @private
37096      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37097      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37098      * @type Number
37099      */
37100     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37101     
37102     /**
37103      * The minimum size of the resizing element. (Defaults to 0)
37104      * @type Number
37105      */
37106     this.minSize = 0;
37107     
37108     /**
37109      * The maximum size of the resizing element. (Defaults to 2000)
37110      * @type Number
37111      */
37112     this.maxSize = 2000;
37113     
37114     /**
37115      * Whether to animate the transition to the new size
37116      * @type Boolean
37117      */
37118     this.animate = false;
37119     
37120     /**
37121      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37122      * @type Boolean
37123      */
37124     this.useShim = false;
37125     
37126     /** @private */
37127     this.shim = null;
37128     
37129     if(!cfg.existingProxy){
37130         /** @private */
37131         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37132     }else{
37133         this.proxy = Roo.get(cfg.existingProxy).dom;
37134     }
37135     /** @private */
37136     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37137     
37138     /** @private */
37139     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37140     
37141     /** @private */
37142     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37143     
37144     /** @private */
37145     this.dragSpecs = {};
37146     
37147     /**
37148      * @private The adapter to use to positon and resize elements
37149      */
37150     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37151     this.adapter.init(this);
37152     
37153     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37154         /** @private */
37155         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37156         this.el.addClass("roo-splitbar-h");
37157     }else{
37158         /** @private */
37159         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37160         this.el.addClass("roo-splitbar-v");
37161     }
37162     
37163     this.addEvents({
37164         /**
37165          * @event resize
37166          * Fires when the splitter is moved (alias for {@link #event-moved})
37167          * @param {Roo.bootstrap.SplitBar} this
37168          * @param {Number} newSize the new width or height
37169          */
37170         "resize" : true,
37171         /**
37172          * @event moved
37173          * Fires when the splitter is moved
37174          * @param {Roo.bootstrap.SplitBar} this
37175          * @param {Number} newSize the new width or height
37176          */
37177         "moved" : true,
37178         /**
37179          * @event beforeresize
37180          * Fires before the splitter is dragged
37181          * @param {Roo.bootstrap.SplitBar} this
37182          */
37183         "beforeresize" : true,
37184
37185         "beforeapply" : true
37186     });
37187
37188     Roo.util.Observable.call(this);
37189 };
37190
37191 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37192     onStartProxyDrag : function(x, y){
37193         this.fireEvent("beforeresize", this);
37194         if(!this.overlay){
37195             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37196             o.unselectable();
37197             o.enableDisplayMode("block");
37198             // all splitbars share the same overlay
37199             Roo.bootstrap.SplitBar.prototype.overlay = o;
37200         }
37201         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37202         this.overlay.show();
37203         Roo.get(this.proxy).setDisplayed("block");
37204         var size = this.adapter.getElementSize(this);
37205         this.activeMinSize = this.getMinimumSize();;
37206         this.activeMaxSize = this.getMaximumSize();;
37207         var c1 = size - this.activeMinSize;
37208         var c2 = Math.max(this.activeMaxSize - size, 0);
37209         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37210             this.dd.resetConstraints();
37211             this.dd.setXConstraint(
37212                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37213                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37214             );
37215             this.dd.setYConstraint(0, 0);
37216         }else{
37217             this.dd.resetConstraints();
37218             this.dd.setXConstraint(0, 0);
37219             this.dd.setYConstraint(
37220                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37221                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37222             );
37223          }
37224         this.dragSpecs.startSize = size;
37225         this.dragSpecs.startPoint = [x, y];
37226         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37227     },
37228     
37229     /** 
37230      * @private Called after the drag operation by the DDProxy
37231      */
37232     onEndProxyDrag : function(e){
37233         Roo.get(this.proxy).setDisplayed(false);
37234         var endPoint = Roo.lib.Event.getXY(e);
37235         if(this.overlay){
37236             this.overlay.hide();
37237         }
37238         var newSize;
37239         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37240             newSize = this.dragSpecs.startSize + 
37241                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37242                     endPoint[0] - this.dragSpecs.startPoint[0] :
37243                     this.dragSpecs.startPoint[0] - endPoint[0]
37244                 );
37245         }else{
37246             newSize = this.dragSpecs.startSize + 
37247                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37248                     endPoint[1] - this.dragSpecs.startPoint[1] :
37249                     this.dragSpecs.startPoint[1] - endPoint[1]
37250                 );
37251         }
37252         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37253         if(newSize != this.dragSpecs.startSize){
37254             if(this.fireEvent('beforeapply', this, newSize) !== false){
37255                 this.adapter.setElementSize(this, newSize);
37256                 this.fireEvent("moved", this, newSize);
37257                 this.fireEvent("resize", this, newSize);
37258             }
37259         }
37260     },
37261     
37262     /**
37263      * Get the adapter this SplitBar uses
37264      * @return The adapter object
37265      */
37266     getAdapter : function(){
37267         return this.adapter;
37268     },
37269     
37270     /**
37271      * Set the adapter this SplitBar uses
37272      * @param {Object} adapter A SplitBar adapter object
37273      */
37274     setAdapter : function(adapter){
37275         this.adapter = adapter;
37276         this.adapter.init(this);
37277     },
37278     
37279     /**
37280      * Gets the minimum size for the resizing element
37281      * @return {Number} The minimum size
37282      */
37283     getMinimumSize : function(){
37284         return this.minSize;
37285     },
37286     
37287     /**
37288      * Sets the minimum size for the resizing element
37289      * @param {Number} minSize The minimum size
37290      */
37291     setMinimumSize : function(minSize){
37292         this.minSize = minSize;
37293     },
37294     
37295     /**
37296      * Gets the maximum size for the resizing element
37297      * @return {Number} The maximum size
37298      */
37299     getMaximumSize : function(){
37300         return this.maxSize;
37301     },
37302     
37303     /**
37304      * Sets the maximum size for the resizing element
37305      * @param {Number} maxSize The maximum size
37306      */
37307     setMaximumSize : function(maxSize){
37308         this.maxSize = maxSize;
37309     },
37310     
37311     /**
37312      * Sets the initialize size for the resizing element
37313      * @param {Number} size The initial size
37314      */
37315     setCurrentSize : function(size){
37316         var oldAnimate = this.animate;
37317         this.animate = false;
37318         this.adapter.setElementSize(this, size);
37319         this.animate = oldAnimate;
37320     },
37321     
37322     /**
37323      * Destroy this splitbar. 
37324      * @param {Boolean} removeEl True to remove the element
37325      */
37326     destroy : function(removeEl){
37327         if(this.shim){
37328             this.shim.remove();
37329         }
37330         this.dd.unreg();
37331         this.proxy.parentNode.removeChild(this.proxy);
37332         if(removeEl){
37333             this.el.remove();
37334         }
37335     }
37336 });
37337
37338 /**
37339  * @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.
37340  */
37341 Roo.bootstrap.SplitBar.createProxy = function(dir){
37342     var proxy = new Roo.Element(document.createElement("div"));
37343     proxy.unselectable();
37344     var cls = 'roo-splitbar-proxy';
37345     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37346     document.body.appendChild(proxy.dom);
37347     return proxy.dom;
37348 };
37349
37350 /** 
37351  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37352  * Default Adapter. It assumes the splitter and resizing element are not positioned
37353  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37354  */
37355 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37356 };
37357
37358 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37359     // do nothing for now
37360     init : function(s){
37361     
37362     },
37363     /**
37364      * Called before drag operations to get the current size of the resizing element. 
37365      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37366      */
37367      getElementSize : function(s){
37368         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37369             return s.resizingEl.getWidth();
37370         }else{
37371             return s.resizingEl.getHeight();
37372         }
37373     },
37374     
37375     /**
37376      * Called after drag operations to set the size of the resizing element.
37377      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37378      * @param {Number} newSize The new size to set
37379      * @param {Function} onComplete A function to be invoked when resizing is complete
37380      */
37381     setElementSize : function(s, newSize, onComplete){
37382         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37383             if(!s.animate){
37384                 s.resizingEl.setWidth(newSize);
37385                 if(onComplete){
37386                     onComplete(s, newSize);
37387                 }
37388             }else{
37389                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37390             }
37391         }else{
37392             
37393             if(!s.animate){
37394                 s.resizingEl.setHeight(newSize);
37395                 if(onComplete){
37396                     onComplete(s, newSize);
37397                 }
37398             }else{
37399                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37400             }
37401         }
37402     }
37403 };
37404
37405 /** 
37406  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37407  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37408  * Adapter that  moves the splitter element to align with the resized sizing element. 
37409  * Used with an absolute positioned SplitBar.
37410  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37411  * document.body, make sure you assign an id to the body element.
37412  */
37413 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37414     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37415     this.container = Roo.get(container);
37416 };
37417
37418 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37419     init : function(s){
37420         this.basic.init(s);
37421     },
37422     
37423     getElementSize : function(s){
37424         return this.basic.getElementSize(s);
37425     },
37426     
37427     setElementSize : function(s, newSize, onComplete){
37428         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37429     },
37430     
37431     moveSplitter : function(s){
37432         var yes = Roo.bootstrap.SplitBar;
37433         switch(s.placement){
37434             case yes.LEFT:
37435                 s.el.setX(s.resizingEl.getRight());
37436                 break;
37437             case yes.RIGHT:
37438                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37439                 break;
37440             case yes.TOP:
37441                 s.el.setY(s.resizingEl.getBottom());
37442                 break;
37443             case yes.BOTTOM:
37444                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37445                 break;
37446         }
37447     }
37448 };
37449
37450 /**
37451  * Orientation constant - Create a vertical SplitBar
37452  * @static
37453  * @type Number
37454  */
37455 Roo.bootstrap.SplitBar.VERTICAL = 1;
37456
37457 /**
37458  * Orientation constant - Create a horizontal SplitBar
37459  * @static
37460  * @type Number
37461  */
37462 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37463
37464 /**
37465  * Placement constant - The resizing element is to the left of the splitter element
37466  * @static
37467  * @type Number
37468  */
37469 Roo.bootstrap.SplitBar.LEFT = 1;
37470
37471 /**
37472  * Placement constant - The resizing element is to the right of the splitter element
37473  * @static
37474  * @type Number
37475  */
37476 Roo.bootstrap.SplitBar.RIGHT = 2;
37477
37478 /**
37479  * Placement constant - The resizing element is positioned above the splitter element
37480  * @static
37481  * @type Number
37482  */
37483 Roo.bootstrap.SplitBar.TOP = 3;
37484
37485 /**
37486  * Placement constant - The resizing element is positioned under splitter element
37487  * @static
37488  * @type Number
37489  */
37490 Roo.bootstrap.SplitBar.BOTTOM = 4;
37491 Roo.namespace("Roo.bootstrap.layout");/*
37492  * Based on:
37493  * Ext JS Library 1.1.1
37494  * Copyright(c) 2006-2007, Ext JS, LLC.
37495  *
37496  * Originally Released Under LGPL - original licence link has changed is not relivant.
37497  *
37498  * Fork - LGPL
37499  * <script type="text/javascript">
37500  */
37501
37502 /**
37503  * @class Roo.bootstrap.layout.Manager
37504  * @extends Roo.bootstrap.Component
37505  * Base class for layout managers.
37506  */
37507 Roo.bootstrap.layout.Manager = function(config)
37508 {
37509     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37510
37511
37512
37513
37514
37515     /** false to disable window resize monitoring @type Boolean */
37516     this.monitorWindowResize = true;
37517     this.regions = {};
37518     this.addEvents({
37519         /**
37520          * @event layout
37521          * Fires when a layout is performed.
37522          * @param {Roo.LayoutManager} this
37523          */
37524         "layout" : true,
37525         /**
37526          * @event regionresized
37527          * Fires when the user resizes a region.
37528          * @param {Roo.LayoutRegion} region The resized region
37529          * @param {Number} newSize The new size (width for east/west, height for north/south)
37530          */
37531         "regionresized" : true,
37532         /**
37533          * @event regioncollapsed
37534          * Fires when a region is collapsed.
37535          * @param {Roo.LayoutRegion} region The collapsed region
37536          */
37537         "regioncollapsed" : true,
37538         /**
37539          * @event regionexpanded
37540          * Fires when a region is expanded.
37541          * @param {Roo.LayoutRegion} region The expanded region
37542          */
37543         "regionexpanded" : true
37544     });
37545     this.updating = false;
37546
37547     if (config.el) {
37548         this.el = Roo.get(config.el);
37549         this.initEvents();
37550     }
37551
37552 };
37553
37554 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37555
37556
37557     regions : null,
37558
37559     monitorWindowResize : true,
37560
37561
37562     updating : false,
37563
37564
37565     onRender : function(ct, position)
37566     {
37567         if(!this.el){
37568             this.el = Roo.get(ct);
37569             this.initEvents();
37570         }
37571         //this.fireEvent('render',this);
37572     },
37573
37574
37575     initEvents: function()
37576     {
37577
37578
37579         // ie scrollbar fix
37580         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37581             document.body.scroll = "no";
37582         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37583             this.el.position('relative');
37584         }
37585         this.id = this.el.id;
37586         this.el.addClass("roo-layout-container");
37587         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37588         if(this.el.dom != document.body ) {
37589             this.el.on('resize', this.layout,this);
37590             this.el.on('show', this.layout,this);
37591         }
37592
37593     },
37594
37595     /**
37596      * Returns true if this layout is currently being updated
37597      * @return {Boolean}
37598      */
37599     isUpdating : function(){
37600         return this.updating;
37601     },
37602
37603     /**
37604      * Suspend the LayoutManager from doing auto-layouts while
37605      * making multiple add or remove calls
37606      */
37607     beginUpdate : function(){
37608         this.updating = true;
37609     },
37610
37611     /**
37612      * Restore auto-layouts and optionally disable the manager from performing a layout
37613      * @param {Boolean} noLayout true to disable a layout update
37614      */
37615     endUpdate : function(noLayout){
37616         this.updating = false;
37617         if(!noLayout){
37618             this.layout();
37619         }
37620     },
37621
37622     layout: function(){
37623         // abstract...
37624     },
37625
37626     onRegionResized : function(region, newSize){
37627         this.fireEvent("regionresized", region, newSize);
37628         this.layout();
37629     },
37630
37631     onRegionCollapsed : function(region){
37632         this.fireEvent("regioncollapsed", region);
37633     },
37634
37635     onRegionExpanded : function(region){
37636         this.fireEvent("regionexpanded", region);
37637     },
37638
37639     /**
37640      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37641      * performs box-model adjustments.
37642      * @return {Object} The size as an object {width: (the width), height: (the height)}
37643      */
37644     getViewSize : function()
37645     {
37646         var size;
37647         if(this.el.dom != document.body){
37648             size = this.el.getSize();
37649         }else{
37650             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37651         }
37652         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37653         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37654         return size;
37655     },
37656
37657     /**
37658      * Returns the Element this layout is bound to.
37659      * @return {Roo.Element}
37660      */
37661     getEl : function(){
37662         return this.el;
37663     },
37664
37665     /**
37666      * Returns the specified region.
37667      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37668      * @return {Roo.LayoutRegion}
37669      */
37670     getRegion : function(target){
37671         return this.regions[target.toLowerCase()];
37672     },
37673
37674     onWindowResize : function(){
37675         if(this.monitorWindowResize){
37676             this.layout();
37677         }
37678     }
37679 });
37680 /*
37681  * Based on:
37682  * Ext JS Library 1.1.1
37683  * Copyright(c) 2006-2007, Ext JS, LLC.
37684  *
37685  * Originally Released Under LGPL - original licence link has changed is not relivant.
37686  *
37687  * Fork - LGPL
37688  * <script type="text/javascript">
37689  */
37690 /**
37691  * @class Roo.bootstrap.layout.Border
37692  * @extends Roo.bootstrap.layout.Manager
37693  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37694  * please see: examples/bootstrap/nested.html<br><br>
37695  
37696 <b>The container the layout is rendered into can be either the body element or any other element.
37697 If it is not the body element, the container needs to either be an absolute positioned element,
37698 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37699 the container size if it is not the body element.</b>
37700
37701 * @constructor
37702 * Create a new Border
37703 * @param {Object} config Configuration options
37704  */
37705 Roo.bootstrap.layout.Border = function(config){
37706     config = config || {};
37707     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37708     
37709     
37710     
37711     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37712         if(config[region]){
37713             config[region].region = region;
37714             this.addRegion(config[region]);
37715         }
37716     },this);
37717     
37718 };
37719
37720 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37721
37722 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37723     
37724     parent : false, // this might point to a 'nest' or a ???
37725     
37726     /**
37727      * Creates and adds a new region if it doesn't already exist.
37728      * @param {String} target The target region key (north, south, east, west or center).
37729      * @param {Object} config The regions config object
37730      * @return {BorderLayoutRegion} The new region
37731      */
37732     addRegion : function(config)
37733     {
37734         if(!this.regions[config.region]){
37735             var r = this.factory(config);
37736             this.bindRegion(r);
37737         }
37738         return this.regions[config.region];
37739     },
37740
37741     // private (kinda)
37742     bindRegion : function(r){
37743         this.regions[r.config.region] = r;
37744         
37745         r.on("visibilitychange",    this.layout, this);
37746         r.on("paneladded",          this.layout, this);
37747         r.on("panelremoved",        this.layout, this);
37748         r.on("invalidated",         this.layout, this);
37749         r.on("resized",             this.onRegionResized, this);
37750         r.on("collapsed",           this.onRegionCollapsed, this);
37751         r.on("expanded",            this.onRegionExpanded, this);
37752     },
37753
37754     /**
37755      * Performs a layout update.
37756      */
37757     layout : function()
37758     {
37759         if(this.updating) {
37760             return;
37761         }
37762         
37763         // render all the rebions if they have not been done alreayd?
37764         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37765             if(this.regions[region] && !this.regions[region].bodyEl){
37766                 this.regions[region].onRender(this.el)
37767             }
37768         },this);
37769         
37770         var size = this.getViewSize();
37771         var w = size.width;
37772         var h = size.height;
37773         var centerW = w;
37774         var centerH = h;
37775         var centerY = 0;
37776         var centerX = 0;
37777         //var x = 0, y = 0;
37778
37779         var rs = this.regions;
37780         var north = rs["north"];
37781         var south = rs["south"]; 
37782         var west = rs["west"];
37783         var east = rs["east"];
37784         var center = rs["center"];
37785         //if(this.hideOnLayout){ // not supported anymore
37786             //c.el.setStyle("display", "none");
37787         //}
37788         if(north && north.isVisible()){
37789             var b = north.getBox();
37790             var m = north.getMargins();
37791             b.width = w - (m.left+m.right);
37792             b.x = m.left;
37793             b.y = m.top;
37794             centerY = b.height + b.y + m.bottom;
37795             centerH -= centerY;
37796             north.updateBox(this.safeBox(b));
37797         }
37798         if(south && south.isVisible()){
37799             var b = south.getBox();
37800             var m = south.getMargins();
37801             b.width = w - (m.left+m.right);
37802             b.x = m.left;
37803             var totalHeight = (b.height + m.top + m.bottom);
37804             b.y = h - totalHeight + m.top;
37805             centerH -= totalHeight;
37806             south.updateBox(this.safeBox(b));
37807         }
37808         if(west && west.isVisible()){
37809             var b = west.getBox();
37810             var m = west.getMargins();
37811             b.height = centerH - (m.top+m.bottom);
37812             b.x = m.left;
37813             b.y = centerY + m.top;
37814             var totalWidth = (b.width + m.left + m.right);
37815             centerX += totalWidth;
37816             centerW -= totalWidth;
37817             west.updateBox(this.safeBox(b));
37818         }
37819         if(east && east.isVisible()){
37820             var b = east.getBox();
37821             var m = east.getMargins();
37822             b.height = centerH - (m.top+m.bottom);
37823             var totalWidth = (b.width + m.left + m.right);
37824             b.x = w - totalWidth + m.left;
37825             b.y = centerY + m.top;
37826             centerW -= totalWidth;
37827             east.updateBox(this.safeBox(b));
37828         }
37829         if(center){
37830             var m = center.getMargins();
37831             var centerBox = {
37832                 x: centerX + m.left,
37833                 y: centerY + m.top,
37834                 width: centerW - (m.left+m.right),
37835                 height: centerH - (m.top+m.bottom)
37836             };
37837             //if(this.hideOnLayout){
37838                 //center.el.setStyle("display", "block");
37839             //}
37840             center.updateBox(this.safeBox(centerBox));
37841         }
37842         this.el.repaint();
37843         this.fireEvent("layout", this);
37844     },
37845
37846     // private
37847     safeBox : function(box){
37848         box.width = Math.max(0, box.width);
37849         box.height = Math.max(0, box.height);
37850         return box;
37851     },
37852
37853     /**
37854      * Adds a ContentPanel (or subclass) to this layout.
37855      * @param {String} target The target region key (north, south, east, west or center).
37856      * @param {Roo.ContentPanel} panel The panel to add
37857      * @return {Roo.ContentPanel} The added panel
37858      */
37859     add : function(target, panel){
37860          
37861         target = target.toLowerCase();
37862         return this.regions[target].add(panel);
37863     },
37864
37865     /**
37866      * Remove a ContentPanel (or subclass) to this layout.
37867      * @param {String} target The target region key (north, south, east, west or center).
37868      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37869      * @return {Roo.ContentPanel} The removed panel
37870      */
37871     remove : function(target, panel){
37872         target = target.toLowerCase();
37873         return this.regions[target].remove(panel);
37874     },
37875
37876     /**
37877      * Searches all regions for a panel with the specified id
37878      * @param {String} panelId
37879      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37880      */
37881     findPanel : function(panelId){
37882         var rs = this.regions;
37883         for(var target in rs){
37884             if(typeof rs[target] != "function"){
37885                 var p = rs[target].getPanel(panelId);
37886                 if(p){
37887                     return p;
37888                 }
37889             }
37890         }
37891         return null;
37892     },
37893
37894     /**
37895      * Searches all regions for a panel with the specified id and activates (shows) it.
37896      * @param {String/ContentPanel} panelId The panels id or the panel itself
37897      * @return {Roo.ContentPanel} The shown panel or null
37898      */
37899     showPanel : function(panelId) {
37900       var rs = this.regions;
37901       for(var target in rs){
37902          var r = rs[target];
37903          if(typeof r != "function"){
37904             if(r.hasPanel(panelId)){
37905                return r.showPanel(panelId);
37906             }
37907          }
37908       }
37909       return null;
37910    },
37911
37912    /**
37913      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37914      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37915      */
37916    /*
37917     restoreState : function(provider){
37918         if(!provider){
37919             provider = Roo.state.Manager;
37920         }
37921         var sm = new Roo.LayoutStateManager();
37922         sm.init(this, provider);
37923     },
37924 */
37925  
37926  
37927     /**
37928      * Adds a xtype elements to the layout.
37929      * <pre><code>
37930
37931 layout.addxtype({
37932        xtype : 'ContentPanel',
37933        region: 'west',
37934        items: [ .... ]
37935    }
37936 );
37937
37938 layout.addxtype({
37939         xtype : 'NestedLayoutPanel',
37940         region: 'west',
37941         layout: {
37942            center: { },
37943            west: { }   
37944         },
37945         items : [ ... list of content panels or nested layout panels.. ]
37946    }
37947 );
37948 </code></pre>
37949      * @param {Object} cfg Xtype definition of item to add.
37950      */
37951     addxtype : function(cfg)
37952     {
37953         // basically accepts a pannel...
37954         // can accept a layout region..!?!?
37955         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37956         
37957         
37958         // theory?  children can only be panels??
37959         
37960         //if (!cfg.xtype.match(/Panel$/)) {
37961         //    return false;
37962         //}
37963         var ret = false;
37964         
37965         if (typeof(cfg.region) == 'undefined') {
37966             Roo.log("Failed to add Panel, region was not set");
37967             Roo.log(cfg);
37968             return false;
37969         }
37970         var region = cfg.region;
37971         delete cfg.region;
37972         
37973           
37974         var xitems = [];
37975         if (cfg.items) {
37976             xitems = cfg.items;
37977             delete cfg.items;
37978         }
37979         var nb = false;
37980         
37981         if ( region == 'center') {
37982             Roo.log("Center: " + cfg.title);
37983         }
37984         
37985         
37986         switch(cfg.xtype) 
37987         {
37988             case 'Content':  // ContentPanel (el, cfg)
37989             case 'Scroll':  // ContentPanel (el, cfg)
37990             case 'View': 
37991                 cfg.autoCreate = cfg.autoCreate || true;
37992                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37993                 //} else {
37994                 //    var el = this.el.createChild();
37995                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37996                 //}
37997                 
37998                 this.add(region, ret);
37999                 break;
38000             
38001             /*
38002             case 'TreePanel': // our new panel!
38003                 cfg.el = this.el.createChild();
38004                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38005                 this.add(region, ret);
38006                 break;
38007             */
38008             
38009             case 'Nest': 
38010                 // create a new Layout (which is  a Border Layout...
38011                 
38012                 var clayout = cfg.layout;
38013                 clayout.el  = this.el.createChild();
38014                 clayout.items   = clayout.items  || [];
38015                 
38016                 delete cfg.layout;
38017                 
38018                 // replace this exitems with the clayout ones..
38019                 xitems = clayout.items;
38020                  
38021                 // force background off if it's in center...
38022                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38023                     cfg.background = false;
38024                 }
38025                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38026                 
38027                 
38028                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38029                 //console.log('adding nested layout panel '  + cfg.toSource());
38030                 this.add(region, ret);
38031                 nb = {}; /// find first...
38032                 break;
38033             
38034             case 'Grid':
38035                 
38036                 // needs grid and region
38037                 
38038                 //var el = this.getRegion(region).el.createChild();
38039                 /*
38040                  *var el = this.el.createChild();
38041                 // create the grid first...
38042                 cfg.grid.container = el;
38043                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38044                 */
38045                 
38046                 if (region == 'center' && this.active ) {
38047                     cfg.background = false;
38048                 }
38049                 
38050                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38051                 
38052                 this.add(region, ret);
38053                 /*
38054                 if (cfg.background) {
38055                     // render grid on panel activation (if panel background)
38056                     ret.on('activate', function(gp) {
38057                         if (!gp.grid.rendered) {
38058                     //        gp.grid.render(el);
38059                         }
38060                     });
38061                 } else {
38062                   //  cfg.grid.render(el);
38063                 }
38064                 */
38065                 break;
38066            
38067            
38068             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38069                 // it was the old xcomponent building that caused this before.
38070                 // espeically if border is the top element in the tree.
38071                 ret = this;
38072                 break; 
38073                 
38074                     
38075                 
38076                 
38077                 
38078             default:
38079                 /*
38080                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38081                     
38082                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38083                     this.add(region, ret);
38084                 } else {
38085                 */
38086                     Roo.log(cfg);
38087                     throw "Can not add '" + cfg.xtype + "' to Border";
38088                     return null;
38089              
38090                                 
38091              
38092         }
38093         this.beginUpdate();
38094         // add children..
38095         var region = '';
38096         var abn = {};
38097         Roo.each(xitems, function(i)  {
38098             region = nb && i.region ? i.region : false;
38099             
38100             var add = ret.addxtype(i);
38101            
38102             if (region) {
38103                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38104                 if (!i.background) {
38105                     abn[region] = nb[region] ;
38106                 }
38107             }
38108             
38109         });
38110         this.endUpdate();
38111
38112         // make the last non-background panel active..
38113         //if (nb) { Roo.log(abn); }
38114         if (nb) {
38115             
38116             for(var r in abn) {
38117                 region = this.getRegion(r);
38118                 if (region) {
38119                     // tried using nb[r], but it does not work..
38120                      
38121                     region.showPanel(abn[r]);
38122                    
38123                 }
38124             }
38125         }
38126         return ret;
38127         
38128     },
38129     
38130     
38131 // private
38132     factory : function(cfg)
38133     {
38134         
38135         var validRegions = Roo.bootstrap.layout.Border.regions;
38136
38137         var target = cfg.region;
38138         cfg.mgr = this;
38139         
38140         var r = Roo.bootstrap.layout;
38141         Roo.log(target);
38142         switch(target){
38143             case "north":
38144                 return new r.North(cfg);
38145             case "south":
38146                 return new r.South(cfg);
38147             case "east":
38148                 return new r.East(cfg);
38149             case "west":
38150                 return new r.West(cfg);
38151             case "center":
38152                 return new r.Center(cfg);
38153         }
38154         throw 'Layout region "'+target+'" not supported.';
38155     }
38156     
38157     
38158 });
38159  /*
38160  * Based on:
38161  * Ext JS Library 1.1.1
38162  * Copyright(c) 2006-2007, Ext JS, LLC.
38163  *
38164  * Originally Released Under LGPL - original licence link has changed is not relivant.
38165  *
38166  * Fork - LGPL
38167  * <script type="text/javascript">
38168  */
38169  
38170 /**
38171  * @class Roo.bootstrap.layout.Basic
38172  * @extends Roo.util.Observable
38173  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38174  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38175  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38176  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38177  * @cfg {string}   region  the region that it inhabits..
38178  * @cfg {bool}   skipConfig skip config?
38179  * 
38180
38181  */
38182 Roo.bootstrap.layout.Basic = function(config){
38183     
38184     this.mgr = config.mgr;
38185     
38186     this.position = config.region;
38187     
38188     var skipConfig = config.skipConfig;
38189     
38190     this.events = {
38191         /**
38192          * @scope Roo.BasicLayoutRegion
38193          */
38194         
38195         /**
38196          * @event beforeremove
38197          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38198          * @param {Roo.LayoutRegion} this
38199          * @param {Roo.ContentPanel} panel The panel
38200          * @param {Object} e The cancel event object
38201          */
38202         "beforeremove" : true,
38203         /**
38204          * @event invalidated
38205          * Fires when the layout for this region is changed.
38206          * @param {Roo.LayoutRegion} this
38207          */
38208         "invalidated" : true,
38209         /**
38210          * @event visibilitychange
38211          * Fires when this region is shown or hidden 
38212          * @param {Roo.LayoutRegion} this
38213          * @param {Boolean} visibility true or false
38214          */
38215         "visibilitychange" : true,
38216         /**
38217          * @event paneladded
38218          * Fires when a panel is added. 
38219          * @param {Roo.LayoutRegion} this
38220          * @param {Roo.ContentPanel} panel The panel
38221          */
38222         "paneladded" : true,
38223         /**
38224          * @event panelremoved
38225          * Fires when a panel is removed. 
38226          * @param {Roo.LayoutRegion} this
38227          * @param {Roo.ContentPanel} panel The panel
38228          */
38229         "panelremoved" : true,
38230         /**
38231          * @event beforecollapse
38232          * Fires when this region before collapse.
38233          * @param {Roo.LayoutRegion} this
38234          */
38235         "beforecollapse" : true,
38236         /**
38237          * @event collapsed
38238          * Fires when this region is collapsed.
38239          * @param {Roo.LayoutRegion} this
38240          */
38241         "collapsed" : true,
38242         /**
38243          * @event expanded
38244          * Fires when this region is expanded.
38245          * @param {Roo.LayoutRegion} this
38246          */
38247         "expanded" : true,
38248         /**
38249          * @event slideshow
38250          * Fires when this region is slid into view.
38251          * @param {Roo.LayoutRegion} this
38252          */
38253         "slideshow" : true,
38254         /**
38255          * @event slidehide
38256          * Fires when this region slides out of view. 
38257          * @param {Roo.LayoutRegion} this
38258          */
38259         "slidehide" : true,
38260         /**
38261          * @event panelactivated
38262          * Fires when a panel is activated. 
38263          * @param {Roo.LayoutRegion} this
38264          * @param {Roo.ContentPanel} panel The activated panel
38265          */
38266         "panelactivated" : true,
38267         /**
38268          * @event resized
38269          * Fires when the user resizes this region. 
38270          * @param {Roo.LayoutRegion} this
38271          * @param {Number} newSize The new size (width for east/west, height for north/south)
38272          */
38273         "resized" : true
38274     };
38275     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38276     this.panels = new Roo.util.MixedCollection();
38277     this.panels.getKey = this.getPanelId.createDelegate(this);
38278     this.box = null;
38279     this.activePanel = null;
38280     // ensure listeners are added...
38281     
38282     if (config.listeners || config.events) {
38283         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38284             listeners : config.listeners || {},
38285             events : config.events || {}
38286         });
38287     }
38288     
38289     if(skipConfig !== true){
38290         this.applyConfig(config);
38291     }
38292 };
38293
38294 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38295 {
38296     getPanelId : function(p){
38297         return p.getId();
38298     },
38299     
38300     applyConfig : function(config){
38301         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38302         this.config = config;
38303         
38304     },
38305     
38306     /**
38307      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38308      * the width, for horizontal (north, south) the height.
38309      * @param {Number} newSize The new width or height
38310      */
38311     resizeTo : function(newSize){
38312         var el = this.el ? this.el :
38313                  (this.activePanel ? this.activePanel.getEl() : null);
38314         if(el){
38315             switch(this.position){
38316                 case "east":
38317                 case "west":
38318                     el.setWidth(newSize);
38319                     this.fireEvent("resized", this, newSize);
38320                 break;
38321                 case "north":
38322                 case "south":
38323                     el.setHeight(newSize);
38324                     this.fireEvent("resized", this, newSize);
38325                 break;                
38326             }
38327         }
38328     },
38329     
38330     getBox : function(){
38331         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38332     },
38333     
38334     getMargins : function(){
38335         return this.margins;
38336     },
38337     
38338     updateBox : function(box){
38339         this.box = box;
38340         var el = this.activePanel.getEl();
38341         el.dom.style.left = box.x + "px";
38342         el.dom.style.top = box.y + "px";
38343         this.activePanel.setSize(box.width, box.height);
38344     },
38345     
38346     /**
38347      * Returns the container element for this region.
38348      * @return {Roo.Element}
38349      */
38350     getEl : function(){
38351         return this.activePanel;
38352     },
38353     
38354     /**
38355      * Returns true if this region is currently visible.
38356      * @return {Boolean}
38357      */
38358     isVisible : function(){
38359         return this.activePanel ? true : false;
38360     },
38361     
38362     setActivePanel : function(panel){
38363         panel = this.getPanel(panel);
38364         if(this.activePanel && this.activePanel != panel){
38365             this.activePanel.setActiveState(false);
38366             this.activePanel.getEl().setLeftTop(-10000,-10000);
38367         }
38368         this.activePanel = panel;
38369         panel.setActiveState(true);
38370         if(this.box){
38371             panel.setSize(this.box.width, this.box.height);
38372         }
38373         this.fireEvent("panelactivated", this, panel);
38374         this.fireEvent("invalidated");
38375     },
38376     
38377     /**
38378      * Show the specified panel.
38379      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38380      * @return {Roo.ContentPanel} The shown panel or null
38381      */
38382     showPanel : function(panel){
38383         panel = this.getPanel(panel);
38384         if(panel){
38385             this.setActivePanel(panel);
38386         }
38387         return panel;
38388     },
38389     
38390     /**
38391      * Get the active panel for this region.
38392      * @return {Roo.ContentPanel} The active panel or null
38393      */
38394     getActivePanel : function(){
38395         return this.activePanel;
38396     },
38397     
38398     /**
38399      * Add the passed ContentPanel(s)
38400      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38401      * @return {Roo.ContentPanel} The panel added (if only one was added)
38402      */
38403     add : function(panel){
38404         if(arguments.length > 1){
38405             for(var i = 0, len = arguments.length; i < len; i++) {
38406                 this.add(arguments[i]);
38407             }
38408             return null;
38409         }
38410         if(this.hasPanel(panel)){
38411             this.showPanel(panel);
38412             return panel;
38413         }
38414         var el = panel.getEl();
38415         if(el.dom.parentNode != this.mgr.el.dom){
38416             this.mgr.el.dom.appendChild(el.dom);
38417         }
38418         if(panel.setRegion){
38419             panel.setRegion(this);
38420         }
38421         this.panels.add(panel);
38422         el.setStyle("position", "absolute");
38423         if(!panel.background){
38424             this.setActivePanel(panel);
38425             if(this.config.initialSize && this.panels.getCount()==1){
38426                 this.resizeTo(this.config.initialSize);
38427             }
38428         }
38429         this.fireEvent("paneladded", this, panel);
38430         return panel;
38431     },
38432     
38433     /**
38434      * Returns true if the panel is in this region.
38435      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38436      * @return {Boolean}
38437      */
38438     hasPanel : function(panel){
38439         if(typeof panel == "object"){ // must be panel obj
38440             panel = panel.getId();
38441         }
38442         return this.getPanel(panel) ? true : false;
38443     },
38444     
38445     /**
38446      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38447      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38448      * @param {Boolean} preservePanel Overrides the config preservePanel option
38449      * @return {Roo.ContentPanel} The panel that was removed
38450      */
38451     remove : function(panel, preservePanel){
38452         panel = this.getPanel(panel);
38453         if(!panel){
38454             return null;
38455         }
38456         var e = {};
38457         this.fireEvent("beforeremove", this, panel, e);
38458         if(e.cancel === true){
38459             return null;
38460         }
38461         var panelId = panel.getId();
38462         this.panels.removeKey(panelId);
38463         return panel;
38464     },
38465     
38466     /**
38467      * Returns the panel specified or null if it's not in this region.
38468      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38469      * @return {Roo.ContentPanel}
38470      */
38471     getPanel : function(id){
38472         if(typeof id == "object"){ // must be panel obj
38473             return id;
38474         }
38475         return this.panels.get(id);
38476     },
38477     
38478     /**
38479      * Returns this regions position (north/south/east/west/center).
38480      * @return {String} 
38481      */
38482     getPosition: function(){
38483         return this.position;    
38484     }
38485 });/*
38486  * Based on:
38487  * Ext JS Library 1.1.1
38488  * Copyright(c) 2006-2007, Ext JS, LLC.
38489  *
38490  * Originally Released Under LGPL - original licence link has changed is not relivant.
38491  *
38492  * Fork - LGPL
38493  * <script type="text/javascript">
38494  */
38495  
38496 /**
38497  * @class Roo.bootstrap.layout.Region
38498  * @extends Roo.bootstrap.layout.Basic
38499  * This class represents a region in a layout manager.
38500  
38501  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38502  * @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})
38503  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38504  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38505  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38506  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38507  * @cfg {String}    title           The title for the region (overrides panel titles)
38508  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38509  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38510  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38511  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38512  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38513  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38514  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38515  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38516  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38517  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38518
38519  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38520  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38521  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38522  * @cfg {Number}    width           For East/West panels
38523  * @cfg {Number}    height          For North/South panels
38524  * @cfg {Boolean}   split           To show the splitter
38525  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38526  * 
38527  * @cfg {string}   cls             Extra CSS classes to add to region
38528  * 
38529  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38530  * @cfg {string}   region  the region that it inhabits..
38531  *
38532
38533  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38534  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38535
38536  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38537  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38538  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38539  */
38540 Roo.bootstrap.layout.Region = function(config)
38541 {
38542     this.applyConfig(config);
38543
38544     var mgr = config.mgr;
38545     var pos = config.region;
38546     config.skipConfig = true;
38547     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38548     
38549     if (mgr.el) {
38550         this.onRender(mgr.el);   
38551     }
38552      
38553     this.visible = true;
38554     this.collapsed = false;
38555     this.unrendered_panels = [];
38556 };
38557
38558 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38559
38560     position: '', // set by wrapper (eg. north/south etc..)
38561     unrendered_panels : null,  // unrendered panels.
38562     
38563     tabPosition : false,
38564     
38565     mgr: false, // points to 'Border'
38566     
38567     
38568     createBody : function(){
38569         /** This region's body element 
38570         * @type Roo.Element */
38571         this.bodyEl = this.el.createChild({
38572                 tag: "div",
38573                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38574         });
38575     },
38576
38577     onRender: function(ctr, pos)
38578     {
38579         var dh = Roo.DomHelper;
38580         /** This region's container element 
38581         * @type Roo.Element */
38582         this.el = dh.append(ctr.dom, {
38583                 tag: "div",
38584                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38585             }, true);
38586         /** This region's title element 
38587         * @type Roo.Element */
38588     
38589         this.titleEl = dh.append(this.el.dom,  {
38590                 tag: "div",
38591                 unselectable: "on",
38592                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38593                 children:[
38594                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38595                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38596                 ]
38597             }, true);
38598         
38599         this.titleEl.enableDisplayMode();
38600         /** This region's title text element 
38601         * @type HTMLElement */
38602         this.titleTextEl = this.titleEl.dom.firstChild;
38603         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38604         /*
38605         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38606         this.closeBtn.enableDisplayMode();
38607         this.closeBtn.on("click", this.closeClicked, this);
38608         this.closeBtn.hide();
38609     */
38610         this.createBody(this.config);
38611         if(this.config.hideWhenEmpty){
38612             this.hide();
38613             this.on("paneladded", this.validateVisibility, this);
38614             this.on("panelremoved", this.validateVisibility, this);
38615         }
38616         if(this.autoScroll){
38617             this.bodyEl.setStyle("overflow", "auto");
38618         }else{
38619             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38620         }
38621         //if(c.titlebar !== false){
38622             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38623                 this.titleEl.hide();
38624             }else{
38625                 this.titleEl.show();
38626                 if(this.config.title){
38627                     this.titleTextEl.innerHTML = this.config.title;
38628                 }
38629             }
38630         //}
38631         if(this.config.collapsed){
38632             this.collapse(true);
38633         }
38634         if(this.config.hidden){
38635             this.hide();
38636         }
38637         
38638         if (this.unrendered_panels && this.unrendered_panels.length) {
38639             for (var i =0;i< this.unrendered_panels.length; i++) {
38640                 this.add(this.unrendered_panels[i]);
38641             }
38642             this.unrendered_panels = null;
38643             
38644         }
38645         
38646     },
38647     
38648     applyConfig : function(c)
38649     {
38650         /*
38651          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38652             var dh = Roo.DomHelper;
38653             if(c.titlebar !== false){
38654                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38655                 this.collapseBtn.on("click", this.collapse, this);
38656                 this.collapseBtn.enableDisplayMode();
38657                 /*
38658                 if(c.showPin === true || this.showPin){
38659                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38660                     this.stickBtn.enableDisplayMode();
38661                     this.stickBtn.on("click", this.expand, this);
38662                     this.stickBtn.hide();
38663                 }
38664                 
38665             }
38666             */
38667             /** This region's collapsed element
38668             * @type Roo.Element */
38669             /*
38670              *
38671             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38672                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38673             ]}, true);
38674             
38675             if(c.floatable !== false){
38676                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38677                this.collapsedEl.on("click", this.collapseClick, this);
38678             }
38679
38680             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38681                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38682                    id: "message", unselectable: "on", style:{"float":"left"}});
38683                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38684              }
38685             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38686             this.expandBtn.on("click", this.expand, this);
38687             
38688         }
38689         
38690         if(this.collapseBtn){
38691             this.collapseBtn.setVisible(c.collapsible == true);
38692         }
38693         
38694         this.cmargins = c.cmargins || this.cmargins ||
38695                          (this.position == "west" || this.position == "east" ?
38696                              {top: 0, left: 2, right:2, bottom: 0} :
38697                              {top: 2, left: 0, right:0, bottom: 2});
38698         */
38699         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38700         
38701         
38702         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38703         
38704         this.autoScroll = c.autoScroll || false;
38705         
38706         
38707        
38708         
38709         this.duration = c.duration || .30;
38710         this.slideDuration = c.slideDuration || .45;
38711         this.config = c;
38712        
38713     },
38714     /**
38715      * Returns true if this region is currently visible.
38716      * @return {Boolean}
38717      */
38718     isVisible : function(){
38719         return this.visible;
38720     },
38721
38722     /**
38723      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38724      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38725      */
38726     //setCollapsedTitle : function(title){
38727     //    title = title || "&#160;";
38728      //   if(this.collapsedTitleTextEl){
38729       //      this.collapsedTitleTextEl.innerHTML = title;
38730        // }
38731     //},
38732
38733     getBox : function(){
38734         var b;
38735       //  if(!this.collapsed){
38736             b = this.el.getBox(false, true);
38737        // }else{
38738           //  b = this.collapsedEl.getBox(false, true);
38739         //}
38740         return b;
38741     },
38742
38743     getMargins : function(){
38744         return this.margins;
38745         //return this.collapsed ? this.cmargins : this.margins;
38746     },
38747 /*
38748     highlight : function(){
38749         this.el.addClass("x-layout-panel-dragover");
38750     },
38751
38752     unhighlight : function(){
38753         this.el.removeClass("x-layout-panel-dragover");
38754     },
38755 */
38756     updateBox : function(box)
38757     {
38758         if (!this.bodyEl) {
38759             return; // not rendered yet..
38760         }
38761         
38762         this.box = box;
38763         if(!this.collapsed){
38764             this.el.dom.style.left = box.x + "px";
38765             this.el.dom.style.top = box.y + "px";
38766             this.updateBody(box.width, box.height);
38767         }else{
38768             this.collapsedEl.dom.style.left = box.x + "px";
38769             this.collapsedEl.dom.style.top = box.y + "px";
38770             this.collapsedEl.setSize(box.width, box.height);
38771         }
38772         if(this.tabs){
38773             this.tabs.autoSizeTabs();
38774         }
38775     },
38776
38777     updateBody : function(w, h)
38778     {
38779         if(w !== null){
38780             this.el.setWidth(w);
38781             w -= this.el.getBorderWidth("rl");
38782             if(this.config.adjustments){
38783                 w += this.config.adjustments[0];
38784             }
38785         }
38786         if(h !== null && h > 0){
38787             this.el.setHeight(h);
38788             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38789             h -= this.el.getBorderWidth("tb");
38790             if(this.config.adjustments){
38791                 h += this.config.adjustments[1];
38792             }
38793             this.bodyEl.setHeight(h);
38794             if(this.tabs){
38795                 h = this.tabs.syncHeight(h);
38796             }
38797         }
38798         if(this.panelSize){
38799             w = w !== null ? w : this.panelSize.width;
38800             h = h !== null ? h : this.panelSize.height;
38801         }
38802         if(this.activePanel){
38803             var el = this.activePanel.getEl();
38804             w = w !== null ? w : el.getWidth();
38805             h = h !== null ? h : el.getHeight();
38806             this.panelSize = {width: w, height: h};
38807             this.activePanel.setSize(w, h);
38808         }
38809         if(Roo.isIE && this.tabs){
38810             this.tabs.el.repaint();
38811         }
38812     },
38813
38814     /**
38815      * Returns the container element for this region.
38816      * @return {Roo.Element}
38817      */
38818     getEl : function(){
38819         return this.el;
38820     },
38821
38822     /**
38823      * Hides this region.
38824      */
38825     hide : function(){
38826         //if(!this.collapsed){
38827             this.el.dom.style.left = "-2000px";
38828             this.el.hide();
38829         //}else{
38830          //   this.collapsedEl.dom.style.left = "-2000px";
38831          //   this.collapsedEl.hide();
38832        // }
38833         this.visible = false;
38834         this.fireEvent("visibilitychange", this, false);
38835     },
38836
38837     /**
38838      * Shows this region if it was previously hidden.
38839      */
38840     show : function(){
38841         //if(!this.collapsed){
38842             this.el.show();
38843         //}else{
38844         //    this.collapsedEl.show();
38845        // }
38846         this.visible = true;
38847         this.fireEvent("visibilitychange", this, true);
38848     },
38849 /*
38850     closeClicked : function(){
38851         if(this.activePanel){
38852             this.remove(this.activePanel);
38853         }
38854     },
38855
38856     collapseClick : function(e){
38857         if(this.isSlid){
38858            e.stopPropagation();
38859            this.slideIn();
38860         }else{
38861            e.stopPropagation();
38862            this.slideOut();
38863         }
38864     },
38865 */
38866     /**
38867      * Collapses this region.
38868      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38869      */
38870     /*
38871     collapse : function(skipAnim, skipCheck = false){
38872         if(this.collapsed) {
38873             return;
38874         }
38875         
38876         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38877             
38878             this.collapsed = true;
38879             if(this.split){
38880                 this.split.el.hide();
38881             }
38882             if(this.config.animate && skipAnim !== true){
38883                 this.fireEvent("invalidated", this);
38884                 this.animateCollapse();
38885             }else{
38886                 this.el.setLocation(-20000,-20000);
38887                 this.el.hide();
38888                 this.collapsedEl.show();
38889                 this.fireEvent("collapsed", this);
38890                 this.fireEvent("invalidated", this);
38891             }
38892         }
38893         
38894     },
38895 */
38896     animateCollapse : function(){
38897         // overridden
38898     },
38899
38900     /**
38901      * Expands this region if it was previously collapsed.
38902      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38903      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38904      */
38905     /*
38906     expand : function(e, skipAnim){
38907         if(e) {
38908             e.stopPropagation();
38909         }
38910         if(!this.collapsed || this.el.hasActiveFx()) {
38911             return;
38912         }
38913         if(this.isSlid){
38914             this.afterSlideIn();
38915             skipAnim = true;
38916         }
38917         this.collapsed = false;
38918         if(this.config.animate && skipAnim !== true){
38919             this.animateExpand();
38920         }else{
38921             this.el.show();
38922             if(this.split){
38923                 this.split.el.show();
38924             }
38925             this.collapsedEl.setLocation(-2000,-2000);
38926             this.collapsedEl.hide();
38927             this.fireEvent("invalidated", this);
38928             this.fireEvent("expanded", this);
38929         }
38930     },
38931 */
38932     animateExpand : function(){
38933         // overridden
38934     },
38935
38936     initTabs : function()
38937     {
38938         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38939         
38940         var ts = new Roo.bootstrap.panel.Tabs({
38941             el: this.bodyEl.dom,
38942             region : this,
38943             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38944             disableTooltips: this.config.disableTabTips,
38945             toolbar : this.config.toolbar
38946         });
38947         
38948         if(this.config.hideTabs){
38949             ts.stripWrap.setDisplayed(false);
38950         }
38951         this.tabs = ts;
38952         ts.resizeTabs = this.config.resizeTabs === true;
38953         ts.minTabWidth = this.config.minTabWidth || 40;
38954         ts.maxTabWidth = this.config.maxTabWidth || 250;
38955         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38956         ts.monitorResize = false;
38957         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38958         ts.bodyEl.addClass('roo-layout-tabs-body');
38959         this.panels.each(this.initPanelAsTab, this);
38960     },
38961
38962     initPanelAsTab : function(panel){
38963         var ti = this.tabs.addTab(
38964             panel.getEl().id,
38965             panel.getTitle(),
38966             null,
38967             this.config.closeOnTab && panel.isClosable(),
38968             panel.tpl
38969         );
38970         if(panel.tabTip !== undefined){
38971             ti.setTooltip(panel.tabTip);
38972         }
38973         ti.on("activate", function(){
38974               this.setActivePanel(panel);
38975         }, this);
38976         
38977         if(this.config.closeOnTab){
38978             ti.on("beforeclose", function(t, e){
38979                 e.cancel = true;
38980                 this.remove(panel);
38981             }, this);
38982         }
38983         
38984         panel.tabItem = ti;
38985         
38986         return ti;
38987     },
38988
38989     updatePanelTitle : function(panel, title)
38990     {
38991         if(this.activePanel == panel){
38992             this.updateTitle(title);
38993         }
38994         if(this.tabs){
38995             var ti = this.tabs.getTab(panel.getEl().id);
38996             ti.setText(title);
38997             if(panel.tabTip !== undefined){
38998                 ti.setTooltip(panel.tabTip);
38999             }
39000         }
39001     },
39002
39003     updateTitle : function(title){
39004         if(this.titleTextEl && !this.config.title){
39005             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39006         }
39007     },
39008
39009     setActivePanel : function(panel)
39010     {
39011         panel = this.getPanel(panel);
39012         if(this.activePanel && this.activePanel != panel){
39013             if(this.activePanel.setActiveState(false) === false){
39014                 return;
39015             }
39016         }
39017         this.activePanel = panel;
39018         panel.setActiveState(true);
39019         if(this.panelSize){
39020             panel.setSize(this.panelSize.width, this.panelSize.height);
39021         }
39022         if(this.closeBtn){
39023             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39024         }
39025         this.updateTitle(panel.getTitle());
39026         if(this.tabs){
39027             this.fireEvent("invalidated", this);
39028         }
39029         this.fireEvent("panelactivated", this, panel);
39030     },
39031
39032     /**
39033      * Shows the specified panel.
39034      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39035      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39036      */
39037     showPanel : function(panel)
39038     {
39039         panel = this.getPanel(panel);
39040         if(panel){
39041             if(this.tabs){
39042                 var tab = this.tabs.getTab(panel.getEl().id);
39043                 if(tab.isHidden()){
39044                     this.tabs.unhideTab(tab.id);
39045                 }
39046                 tab.activate();
39047             }else{
39048                 this.setActivePanel(panel);
39049             }
39050         }
39051         return panel;
39052     },
39053
39054     /**
39055      * Get the active panel for this region.
39056      * @return {Roo.ContentPanel} The active panel or null
39057      */
39058     getActivePanel : function(){
39059         return this.activePanel;
39060     },
39061
39062     validateVisibility : function(){
39063         if(this.panels.getCount() < 1){
39064             this.updateTitle("&#160;");
39065             this.closeBtn.hide();
39066             this.hide();
39067         }else{
39068             if(!this.isVisible()){
39069                 this.show();
39070             }
39071         }
39072     },
39073
39074     /**
39075      * Adds the passed ContentPanel(s) to this region.
39076      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39077      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39078      */
39079     add : function(panel)
39080     {
39081         if(arguments.length > 1){
39082             for(var i = 0, len = arguments.length; i < len; i++) {
39083                 this.add(arguments[i]);
39084             }
39085             return null;
39086         }
39087         
39088         // if we have not been rendered yet, then we can not really do much of this..
39089         if (!this.bodyEl) {
39090             this.unrendered_panels.push(panel);
39091             return panel;
39092         }
39093         
39094         
39095         
39096         
39097         if(this.hasPanel(panel)){
39098             this.showPanel(panel);
39099             return panel;
39100         }
39101         panel.setRegion(this);
39102         this.panels.add(panel);
39103        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39104             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39105             // and hide them... ???
39106             this.bodyEl.dom.appendChild(panel.getEl().dom);
39107             if(panel.background !== true){
39108                 this.setActivePanel(panel);
39109             }
39110             this.fireEvent("paneladded", this, panel);
39111             return panel;
39112         }
39113         */
39114         if(!this.tabs){
39115             this.initTabs();
39116         }else{
39117             this.initPanelAsTab(panel);
39118         }
39119         
39120         
39121         if(panel.background !== true){
39122             this.tabs.activate(panel.getEl().id);
39123         }
39124         this.fireEvent("paneladded", this, panel);
39125         return panel;
39126     },
39127
39128     /**
39129      * Hides the tab for the specified panel.
39130      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39131      */
39132     hidePanel : function(panel){
39133         if(this.tabs && (panel = this.getPanel(panel))){
39134             this.tabs.hideTab(panel.getEl().id);
39135         }
39136     },
39137
39138     /**
39139      * Unhides the tab for a previously hidden panel.
39140      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39141      */
39142     unhidePanel : function(panel){
39143         if(this.tabs && (panel = this.getPanel(panel))){
39144             this.tabs.unhideTab(panel.getEl().id);
39145         }
39146     },
39147
39148     clearPanels : function(){
39149         while(this.panels.getCount() > 0){
39150              this.remove(this.panels.first());
39151         }
39152     },
39153
39154     /**
39155      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39156      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39157      * @param {Boolean} preservePanel Overrides the config preservePanel option
39158      * @return {Roo.ContentPanel} The panel that was removed
39159      */
39160     remove : function(panel, preservePanel)
39161     {
39162         panel = this.getPanel(panel);
39163         if(!panel){
39164             return null;
39165         }
39166         var e = {};
39167         this.fireEvent("beforeremove", this, panel, e);
39168         if(e.cancel === true){
39169             return null;
39170         }
39171         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39172         var panelId = panel.getId();
39173         this.panels.removeKey(panelId);
39174         if(preservePanel){
39175             document.body.appendChild(panel.getEl().dom);
39176         }
39177         if(this.tabs){
39178             this.tabs.removeTab(panel.getEl().id);
39179         }else if (!preservePanel){
39180             this.bodyEl.dom.removeChild(panel.getEl().dom);
39181         }
39182         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39183             var p = this.panels.first();
39184             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39185             tempEl.appendChild(p.getEl().dom);
39186             this.bodyEl.update("");
39187             this.bodyEl.dom.appendChild(p.getEl().dom);
39188             tempEl = null;
39189             this.updateTitle(p.getTitle());
39190             this.tabs = null;
39191             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39192             this.setActivePanel(p);
39193         }
39194         panel.setRegion(null);
39195         if(this.activePanel == panel){
39196             this.activePanel = null;
39197         }
39198         if(this.config.autoDestroy !== false && preservePanel !== true){
39199             try{panel.destroy();}catch(e){}
39200         }
39201         this.fireEvent("panelremoved", this, panel);
39202         return panel;
39203     },
39204
39205     /**
39206      * Returns the TabPanel component used by this region
39207      * @return {Roo.TabPanel}
39208      */
39209     getTabs : function(){
39210         return this.tabs;
39211     },
39212
39213     createTool : function(parentEl, className){
39214         var btn = Roo.DomHelper.append(parentEl, {
39215             tag: "div",
39216             cls: "x-layout-tools-button",
39217             children: [ {
39218                 tag: "div",
39219                 cls: "roo-layout-tools-button-inner " + className,
39220                 html: "&#160;"
39221             }]
39222         }, true);
39223         btn.addClassOnOver("roo-layout-tools-button-over");
39224         return btn;
39225     }
39226 });/*
39227  * Based on:
39228  * Ext JS Library 1.1.1
39229  * Copyright(c) 2006-2007, Ext JS, LLC.
39230  *
39231  * Originally Released Under LGPL - original licence link has changed is not relivant.
39232  *
39233  * Fork - LGPL
39234  * <script type="text/javascript">
39235  */
39236  
39237
39238
39239 /**
39240  * @class Roo.SplitLayoutRegion
39241  * @extends Roo.LayoutRegion
39242  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39243  */
39244 Roo.bootstrap.layout.Split = function(config){
39245     this.cursor = config.cursor;
39246     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39247 };
39248
39249 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39250 {
39251     splitTip : "Drag to resize.",
39252     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39253     useSplitTips : false,
39254
39255     applyConfig : function(config){
39256         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39257     },
39258     
39259     onRender : function(ctr,pos) {
39260         
39261         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39262         if(!this.config.split){
39263             return;
39264         }
39265         if(!this.split){
39266             
39267             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39268                             tag: "div",
39269                             id: this.el.id + "-split",
39270                             cls: "roo-layout-split roo-layout-split-"+this.position,
39271                             html: "&#160;"
39272             });
39273             /** The SplitBar for this region 
39274             * @type Roo.SplitBar */
39275             // does not exist yet...
39276             Roo.log([this.position, this.orientation]);
39277             
39278             this.split = new Roo.bootstrap.SplitBar({
39279                 dragElement : splitEl,
39280                 resizingElement: this.el,
39281                 orientation : this.orientation
39282             });
39283             
39284             this.split.on("moved", this.onSplitMove, this);
39285             this.split.useShim = this.config.useShim === true;
39286             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39287             if(this.useSplitTips){
39288                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39289             }
39290             //if(config.collapsible){
39291             //    this.split.el.on("dblclick", this.collapse,  this);
39292             //}
39293         }
39294         if(typeof this.config.minSize != "undefined"){
39295             this.split.minSize = this.config.minSize;
39296         }
39297         if(typeof this.config.maxSize != "undefined"){
39298             this.split.maxSize = this.config.maxSize;
39299         }
39300         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39301             this.hideSplitter();
39302         }
39303         
39304     },
39305
39306     getHMaxSize : function(){
39307          var cmax = this.config.maxSize || 10000;
39308          var center = this.mgr.getRegion("center");
39309          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39310     },
39311
39312     getVMaxSize : function(){
39313          var cmax = this.config.maxSize || 10000;
39314          var center = this.mgr.getRegion("center");
39315          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39316     },
39317
39318     onSplitMove : function(split, newSize){
39319         this.fireEvent("resized", this, newSize);
39320     },
39321     
39322     /** 
39323      * Returns the {@link Roo.SplitBar} for this region.
39324      * @return {Roo.SplitBar}
39325      */
39326     getSplitBar : function(){
39327         return this.split;
39328     },
39329     
39330     hide : function(){
39331         this.hideSplitter();
39332         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39333     },
39334
39335     hideSplitter : function(){
39336         if(this.split){
39337             this.split.el.setLocation(-2000,-2000);
39338             this.split.el.hide();
39339         }
39340     },
39341
39342     show : function(){
39343         if(this.split){
39344             this.split.el.show();
39345         }
39346         Roo.bootstrap.layout.Split.superclass.show.call(this);
39347     },
39348     
39349     beforeSlide: function(){
39350         if(Roo.isGecko){// firefox overflow auto bug workaround
39351             this.bodyEl.clip();
39352             if(this.tabs) {
39353                 this.tabs.bodyEl.clip();
39354             }
39355             if(this.activePanel){
39356                 this.activePanel.getEl().clip();
39357                 
39358                 if(this.activePanel.beforeSlide){
39359                     this.activePanel.beforeSlide();
39360                 }
39361             }
39362         }
39363     },
39364     
39365     afterSlide : function(){
39366         if(Roo.isGecko){// firefox overflow auto bug workaround
39367             this.bodyEl.unclip();
39368             if(this.tabs) {
39369                 this.tabs.bodyEl.unclip();
39370             }
39371             if(this.activePanel){
39372                 this.activePanel.getEl().unclip();
39373                 if(this.activePanel.afterSlide){
39374                     this.activePanel.afterSlide();
39375                 }
39376             }
39377         }
39378     },
39379
39380     initAutoHide : function(){
39381         if(this.autoHide !== false){
39382             if(!this.autoHideHd){
39383                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39384                 this.autoHideHd = {
39385                     "mouseout": function(e){
39386                         if(!e.within(this.el, true)){
39387                             st.delay(500);
39388                         }
39389                     },
39390                     "mouseover" : function(e){
39391                         st.cancel();
39392                     },
39393                     scope : this
39394                 };
39395             }
39396             this.el.on(this.autoHideHd);
39397         }
39398     },
39399
39400     clearAutoHide : function(){
39401         if(this.autoHide !== false){
39402             this.el.un("mouseout", this.autoHideHd.mouseout);
39403             this.el.un("mouseover", this.autoHideHd.mouseover);
39404         }
39405     },
39406
39407     clearMonitor : function(){
39408         Roo.get(document).un("click", this.slideInIf, this);
39409     },
39410
39411     // these names are backwards but not changed for compat
39412     slideOut : function(){
39413         if(this.isSlid || this.el.hasActiveFx()){
39414             return;
39415         }
39416         this.isSlid = true;
39417         if(this.collapseBtn){
39418             this.collapseBtn.hide();
39419         }
39420         this.closeBtnState = this.closeBtn.getStyle('display');
39421         this.closeBtn.hide();
39422         if(this.stickBtn){
39423             this.stickBtn.show();
39424         }
39425         this.el.show();
39426         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39427         this.beforeSlide();
39428         this.el.setStyle("z-index", 10001);
39429         this.el.slideIn(this.getSlideAnchor(), {
39430             callback: function(){
39431                 this.afterSlide();
39432                 this.initAutoHide();
39433                 Roo.get(document).on("click", this.slideInIf, this);
39434                 this.fireEvent("slideshow", this);
39435             },
39436             scope: this,
39437             block: true
39438         });
39439     },
39440
39441     afterSlideIn : function(){
39442         this.clearAutoHide();
39443         this.isSlid = false;
39444         this.clearMonitor();
39445         this.el.setStyle("z-index", "");
39446         if(this.collapseBtn){
39447             this.collapseBtn.show();
39448         }
39449         this.closeBtn.setStyle('display', this.closeBtnState);
39450         if(this.stickBtn){
39451             this.stickBtn.hide();
39452         }
39453         this.fireEvent("slidehide", this);
39454     },
39455
39456     slideIn : function(cb){
39457         if(!this.isSlid || this.el.hasActiveFx()){
39458             Roo.callback(cb);
39459             return;
39460         }
39461         this.isSlid = false;
39462         this.beforeSlide();
39463         this.el.slideOut(this.getSlideAnchor(), {
39464             callback: function(){
39465                 this.el.setLeftTop(-10000, -10000);
39466                 this.afterSlide();
39467                 this.afterSlideIn();
39468                 Roo.callback(cb);
39469             },
39470             scope: this,
39471             block: true
39472         });
39473     },
39474     
39475     slideInIf : function(e){
39476         if(!e.within(this.el)){
39477             this.slideIn();
39478         }
39479     },
39480
39481     animateCollapse : function(){
39482         this.beforeSlide();
39483         this.el.setStyle("z-index", 20000);
39484         var anchor = this.getSlideAnchor();
39485         this.el.slideOut(anchor, {
39486             callback : function(){
39487                 this.el.setStyle("z-index", "");
39488                 this.collapsedEl.slideIn(anchor, {duration:.3});
39489                 this.afterSlide();
39490                 this.el.setLocation(-10000,-10000);
39491                 this.el.hide();
39492                 this.fireEvent("collapsed", this);
39493             },
39494             scope: this,
39495             block: true
39496         });
39497     },
39498
39499     animateExpand : function(){
39500         this.beforeSlide();
39501         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39502         this.el.setStyle("z-index", 20000);
39503         this.collapsedEl.hide({
39504             duration:.1
39505         });
39506         this.el.slideIn(this.getSlideAnchor(), {
39507             callback : function(){
39508                 this.el.setStyle("z-index", "");
39509                 this.afterSlide();
39510                 if(this.split){
39511                     this.split.el.show();
39512                 }
39513                 this.fireEvent("invalidated", this);
39514                 this.fireEvent("expanded", this);
39515             },
39516             scope: this,
39517             block: true
39518         });
39519     },
39520
39521     anchors : {
39522         "west" : "left",
39523         "east" : "right",
39524         "north" : "top",
39525         "south" : "bottom"
39526     },
39527
39528     sanchors : {
39529         "west" : "l",
39530         "east" : "r",
39531         "north" : "t",
39532         "south" : "b"
39533     },
39534
39535     canchors : {
39536         "west" : "tl-tr",
39537         "east" : "tr-tl",
39538         "north" : "tl-bl",
39539         "south" : "bl-tl"
39540     },
39541
39542     getAnchor : function(){
39543         return this.anchors[this.position];
39544     },
39545
39546     getCollapseAnchor : function(){
39547         return this.canchors[this.position];
39548     },
39549
39550     getSlideAnchor : function(){
39551         return this.sanchors[this.position];
39552     },
39553
39554     getAlignAdj : function(){
39555         var cm = this.cmargins;
39556         switch(this.position){
39557             case "west":
39558                 return [0, 0];
39559             break;
39560             case "east":
39561                 return [0, 0];
39562             break;
39563             case "north":
39564                 return [0, 0];
39565             break;
39566             case "south":
39567                 return [0, 0];
39568             break;
39569         }
39570     },
39571
39572     getExpandAdj : function(){
39573         var c = this.collapsedEl, cm = this.cmargins;
39574         switch(this.position){
39575             case "west":
39576                 return [-(cm.right+c.getWidth()+cm.left), 0];
39577             break;
39578             case "east":
39579                 return [cm.right+c.getWidth()+cm.left, 0];
39580             break;
39581             case "north":
39582                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39583             break;
39584             case "south":
39585                 return [0, cm.top+cm.bottom+c.getHeight()];
39586             break;
39587         }
39588     }
39589 });/*
39590  * Based on:
39591  * Ext JS Library 1.1.1
39592  * Copyright(c) 2006-2007, Ext JS, LLC.
39593  *
39594  * Originally Released Under LGPL - original licence link has changed is not relivant.
39595  *
39596  * Fork - LGPL
39597  * <script type="text/javascript">
39598  */
39599 /*
39600  * These classes are private internal classes
39601  */
39602 Roo.bootstrap.layout.Center = function(config){
39603     config.region = "center";
39604     Roo.bootstrap.layout.Region.call(this, config);
39605     this.visible = true;
39606     this.minWidth = config.minWidth || 20;
39607     this.minHeight = config.minHeight || 20;
39608 };
39609
39610 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39611     hide : function(){
39612         // center panel can't be hidden
39613     },
39614     
39615     show : function(){
39616         // center panel can't be hidden
39617     },
39618     
39619     getMinWidth: function(){
39620         return this.minWidth;
39621     },
39622     
39623     getMinHeight: function(){
39624         return this.minHeight;
39625     }
39626 });
39627
39628
39629
39630
39631  
39632
39633
39634
39635
39636
39637
39638 Roo.bootstrap.layout.North = function(config)
39639 {
39640     config.region = 'north';
39641     config.cursor = 'n-resize';
39642     
39643     Roo.bootstrap.layout.Split.call(this, config);
39644     
39645     
39646     if(this.split){
39647         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39648         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39649         this.split.el.addClass("roo-layout-split-v");
39650     }
39651     //var size = config.initialSize || config.height;
39652     //if(this.el && typeof size != "undefined"){
39653     //    this.el.setHeight(size);
39654     //}
39655 };
39656 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39657 {
39658     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39659      
39660      
39661     onRender : function(ctr, pos)
39662     {
39663         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39664         var size = this.config.initialSize || this.config.height;
39665         if(this.el && typeof size != "undefined"){
39666             this.el.setHeight(size);
39667         }
39668     
39669     },
39670     
39671     getBox : function(){
39672         if(this.collapsed){
39673             return this.collapsedEl.getBox();
39674         }
39675         var box = this.el.getBox();
39676         if(this.split){
39677             box.height += this.split.el.getHeight();
39678         }
39679         return box;
39680     },
39681     
39682     updateBox : function(box){
39683         if(this.split && !this.collapsed){
39684             box.height -= this.split.el.getHeight();
39685             this.split.el.setLeft(box.x);
39686             this.split.el.setTop(box.y+box.height);
39687             this.split.el.setWidth(box.width);
39688         }
39689         if(this.collapsed){
39690             this.updateBody(box.width, null);
39691         }
39692         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39693     }
39694 });
39695
39696
39697
39698
39699
39700 Roo.bootstrap.layout.South = function(config){
39701     config.region = 'south';
39702     config.cursor = 's-resize';
39703     Roo.bootstrap.layout.Split.call(this, config);
39704     if(this.split){
39705         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39706         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39707         this.split.el.addClass("roo-layout-split-v");
39708     }
39709     
39710 };
39711
39712 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39713     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39714     
39715     onRender : function(ctr, pos)
39716     {
39717         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39718         var size = this.config.initialSize || this.config.height;
39719         if(this.el && typeof size != "undefined"){
39720             this.el.setHeight(size);
39721         }
39722     
39723     },
39724     
39725     getBox : function(){
39726         if(this.collapsed){
39727             return this.collapsedEl.getBox();
39728         }
39729         var box = this.el.getBox();
39730         if(this.split){
39731             var sh = this.split.el.getHeight();
39732             box.height += sh;
39733             box.y -= sh;
39734         }
39735         return box;
39736     },
39737     
39738     updateBox : function(box){
39739         if(this.split && !this.collapsed){
39740             var sh = this.split.el.getHeight();
39741             box.height -= sh;
39742             box.y += sh;
39743             this.split.el.setLeft(box.x);
39744             this.split.el.setTop(box.y-sh);
39745             this.split.el.setWidth(box.width);
39746         }
39747         if(this.collapsed){
39748             this.updateBody(box.width, null);
39749         }
39750         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39751     }
39752 });
39753
39754 Roo.bootstrap.layout.East = function(config){
39755     config.region = "east";
39756     config.cursor = "e-resize";
39757     Roo.bootstrap.layout.Split.call(this, config);
39758     if(this.split){
39759         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39760         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39761         this.split.el.addClass("roo-layout-split-h");
39762     }
39763     
39764 };
39765 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39766     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39767     
39768     onRender : function(ctr, pos)
39769     {
39770         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39771         var size = this.config.initialSize || this.config.width;
39772         if(this.el && typeof size != "undefined"){
39773             this.el.setWidth(size);
39774         }
39775     
39776     },
39777     
39778     getBox : function(){
39779         if(this.collapsed){
39780             return this.collapsedEl.getBox();
39781         }
39782         var box = this.el.getBox();
39783         if(this.split){
39784             var sw = this.split.el.getWidth();
39785             box.width += sw;
39786             box.x -= sw;
39787         }
39788         return box;
39789     },
39790
39791     updateBox : function(box){
39792         if(this.split && !this.collapsed){
39793             var sw = this.split.el.getWidth();
39794             box.width -= sw;
39795             this.split.el.setLeft(box.x);
39796             this.split.el.setTop(box.y);
39797             this.split.el.setHeight(box.height);
39798             box.x += sw;
39799         }
39800         if(this.collapsed){
39801             this.updateBody(null, box.height);
39802         }
39803         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39804     }
39805 });
39806
39807 Roo.bootstrap.layout.West = function(config){
39808     config.region = "west";
39809     config.cursor = "w-resize";
39810     
39811     Roo.bootstrap.layout.Split.call(this, config);
39812     if(this.split){
39813         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39814         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39815         this.split.el.addClass("roo-layout-split-h");
39816     }
39817     
39818 };
39819 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39820     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39821     
39822     onRender: function(ctr, pos)
39823     {
39824         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39825         var size = this.config.initialSize || this.config.width;
39826         if(typeof size != "undefined"){
39827             this.el.setWidth(size);
39828         }
39829     },
39830     
39831     getBox : function(){
39832         if(this.collapsed){
39833             return this.collapsedEl.getBox();
39834         }
39835         var box = this.el.getBox();
39836         if (box.width == 0) {
39837             box.width = this.config.width; // kludge?
39838         }
39839         if(this.split){
39840             box.width += this.split.el.getWidth();
39841         }
39842         return box;
39843     },
39844     
39845     updateBox : function(box){
39846         if(this.split && !this.collapsed){
39847             var sw = this.split.el.getWidth();
39848             box.width -= sw;
39849             this.split.el.setLeft(box.x+box.width);
39850             this.split.el.setTop(box.y);
39851             this.split.el.setHeight(box.height);
39852         }
39853         if(this.collapsed){
39854             this.updateBody(null, box.height);
39855         }
39856         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39857     }
39858 });Roo.namespace("Roo.bootstrap.panel");/*
39859  * Based on:
39860  * Ext JS Library 1.1.1
39861  * Copyright(c) 2006-2007, Ext JS, LLC.
39862  *
39863  * Originally Released Under LGPL - original licence link has changed is not relivant.
39864  *
39865  * Fork - LGPL
39866  * <script type="text/javascript">
39867  */
39868 /**
39869  * @class Roo.ContentPanel
39870  * @extends Roo.util.Observable
39871  * A basic ContentPanel element.
39872  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39873  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39874  * @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
39875  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39876  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39877  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39878  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39879  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39880  * @cfg {String} title          The title for this panel
39881  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39882  * @cfg {String} url            Calls {@link #setUrl} with this value
39883  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39884  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39885  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39886  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39887  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39888  * @cfg {Boolean} badges render the badges
39889  * @cfg {String} cls  extra classes to use  
39890  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39891
39892  * @constructor
39893  * Create a new ContentPanel.
39894  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39895  * @param {String/Object} config A string to set only the title or a config object
39896  * @param {String} content (optional) Set the HTML content for this panel
39897  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39898  */
39899 Roo.bootstrap.panel.Content = function( config){
39900     
39901     this.tpl = config.tpl || false;
39902     
39903     var el = config.el;
39904     var content = config.content;
39905
39906     if(config.autoCreate){ // xtype is available if this is called from factory
39907         el = Roo.id();
39908     }
39909     this.el = Roo.get(el);
39910     if(!this.el && config && config.autoCreate){
39911         if(typeof config.autoCreate == "object"){
39912             if(!config.autoCreate.id){
39913                 config.autoCreate.id = config.id||el;
39914             }
39915             this.el = Roo.DomHelper.append(document.body,
39916                         config.autoCreate, true);
39917         }else{
39918             var elcfg =  {
39919                 tag: "div",
39920                 cls: (config.cls || '') +
39921                     (config.background ? ' bg-' + config.background : '') +
39922                     " roo-layout-inactive-content",
39923                 id: config.id||el
39924             };
39925             if (config.iframe) {
39926                 elcfg.cn = [
39927                     {
39928                         tag : 'iframe',
39929                         style : 'border: 0px',
39930                         src : 'about:blank'
39931                     }
39932                 ];
39933             }
39934               
39935             if (config.html) {
39936                 elcfg.html = config.html;
39937                 
39938             }
39939                         
39940             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39941             if (config.iframe) {
39942                 this.iframeEl = this.el.select('iframe',true).first();
39943             }
39944             
39945         }
39946     } 
39947     this.closable = false;
39948     this.loaded = false;
39949     this.active = false;
39950    
39951       
39952     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39953         
39954         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39955         
39956         this.wrapEl = this.el; //this.el.wrap();
39957         var ti = [];
39958         if (config.toolbar.items) {
39959             ti = config.toolbar.items ;
39960             delete config.toolbar.items ;
39961         }
39962         
39963         var nitems = [];
39964         this.toolbar.render(this.wrapEl, 'before');
39965         for(var i =0;i < ti.length;i++) {
39966           //  Roo.log(['add child', items[i]]);
39967             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39968         }
39969         this.toolbar.items = nitems;
39970         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39971         delete config.toolbar;
39972         
39973     }
39974     /*
39975     // xtype created footer. - not sure if will work as we normally have to render first..
39976     if (this.footer && !this.footer.el && this.footer.xtype) {
39977         if (!this.wrapEl) {
39978             this.wrapEl = this.el.wrap();
39979         }
39980     
39981         this.footer.container = this.wrapEl.createChild();
39982          
39983         this.footer = Roo.factory(this.footer, Roo);
39984         
39985     }
39986     */
39987     
39988      if(typeof config == "string"){
39989         this.title = config;
39990     }else{
39991         Roo.apply(this, config);
39992     }
39993     
39994     if(this.resizeEl){
39995         this.resizeEl = Roo.get(this.resizeEl, true);
39996     }else{
39997         this.resizeEl = this.el;
39998     }
39999     // handle view.xtype
40000     
40001  
40002     
40003     
40004     this.addEvents({
40005         /**
40006          * @event activate
40007          * Fires when this panel is activated. 
40008          * @param {Roo.ContentPanel} this
40009          */
40010         "activate" : true,
40011         /**
40012          * @event deactivate
40013          * Fires when this panel is activated. 
40014          * @param {Roo.ContentPanel} this
40015          */
40016         "deactivate" : true,
40017
40018         /**
40019          * @event resize
40020          * Fires when this panel is resized if fitToFrame is true.
40021          * @param {Roo.ContentPanel} this
40022          * @param {Number} width The width after any component adjustments
40023          * @param {Number} height The height after any component adjustments
40024          */
40025         "resize" : true,
40026         
40027          /**
40028          * @event render
40029          * Fires when this tab is created
40030          * @param {Roo.ContentPanel} this
40031          */
40032         "render" : true
40033         
40034         
40035         
40036     });
40037     
40038
40039     
40040     
40041     if(this.autoScroll && !this.iframe){
40042         this.resizeEl.setStyle("overflow", "auto");
40043     } else {
40044         // fix randome scrolling
40045         //this.el.on('scroll', function() {
40046         //    Roo.log('fix random scolling');
40047         //    this.scrollTo('top',0); 
40048         //});
40049     }
40050     content = content || this.content;
40051     if(content){
40052         this.setContent(content);
40053     }
40054     if(config && config.url){
40055         this.setUrl(this.url, this.params, this.loadOnce);
40056     }
40057     
40058     
40059     
40060     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40061     
40062     if (this.view && typeof(this.view.xtype) != 'undefined') {
40063         this.view.el = this.el.appendChild(document.createElement("div"));
40064         this.view = Roo.factory(this.view); 
40065         this.view.render  &&  this.view.render(false, '');  
40066     }
40067     
40068     
40069     this.fireEvent('render', this);
40070 };
40071
40072 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40073     
40074     cls : '',
40075     background : '',
40076     
40077     tabTip : '',
40078     
40079     iframe : false,
40080     iframeEl : false,
40081     
40082     setRegion : function(region){
40083         this.region = region;
40084         this.setActiveClass(region && !this.background);
40085     },
40086     
40087     
40088     setActiveClass: function(state)
40089     {
40090         if(state){
40091            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40092            this.el.setStyle('position','relative');
40093         }else{
40094            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40095            this.el.setStyle('position', 'absolute');
40096         } 
40097     },
40098     
40099     /**
40100      * Returns the toolbar for this Panel if one was configured. 
40101      * @return {Roo.Toolbar} 
40102      */
40103     getToolbar : function(){
40104         return this.toolbar;
40105     },
40106     
40107     setActiveState : function(active)
40108     {
40109         this.active = active;
40110         this.setActiveClass(active);
40111         if(!active){
40112             if(this.fireEvent("deactivate", this) === false){
40113                 return false;
40114             }
40115             return true;
40116         }
40117         this.fireEvent("activate", this);
40118         return true;
40119     },
40120     /**
40121      * Updates this panel's element (not for iframe)
40122      * @param {String} content The new content
40123      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40124     */
40125     setContent : function(content, loadScripts){
40126         if (this.iframe) {
40127             return;
40128         }
40129         
40130         this.el.update(content, loadScripts);
40131     },
40132
40133     ignoreResize : function(w, h){
40134         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40135             return true;
40136         }else{
40137             this.lastSize = {width: w, height: h};
40138             return false;
40139         }
40140     },
40141     /**
40142      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40143      * @return {Roo.UpdateManager} The UpdateManager
40144      */
40145     getUpdateManager : function(){
40146         if (this.iframe) {
40147             return false;
40148         }
40149         return this.el.getUpdateManager();
40150     },
40151      /**
40152      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40153      * Does not work with IFRAME contents
40154      * @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:
40155 <pre><code>
40156 panel.load({
40157     url: "your-url.php",
40158     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40159     callback: yourFunction,
40160     scope: yourObject, //(optional scope)
40161     discardUrl: false,
40162     nocache: false,
40163     text: "Loading...",
40164     timeout: 30,
40165     scripts: false
40166 });
40167 </code></pre>
40168      
40169      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40170      * 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.
40171      * @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}
40172      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40173      * @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.
40174      * @return {Roo.ContentPanel} this
40175      */
40176     load : function(){
40177         
40178         if (this.iframe) {
40179             return this;
40180         }
40181         
40182         var um = this.el.getUpdateManager();
40183         um.update.apply(um, arguments);
40184         return this;
40185     },
40186
40187
40188     /**
40189      * 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.
40190      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40191      * @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)
40192      * @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)
40193      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40194      */
40195     setUrl : function(url, params, loadOnce){
40196         if (this.iframe) {
40197             this.iframeEl.dom.src = url;
40198             return false;
40199         }
40200         
40201         if(this.refreshDelegate){
40202             this.removeListener("activate", this.refreshDelegate);
40203         }
40204         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40205         this.on("activate", this.refreshDelegate);
40206         return this.el.getUpdateManager();
40207     },
40208     
40209     _handleRefresh : function(url, params, loadOnce){
40210         if(!loadOnce || !this.loaded){
40211             var updater = this.el.getUpdateManager();
40212             updater.update(url, params, this._setLoaded.createDelegate(this));
40213         }
40214     },
40215     
40216     _setLoaded : function(){
40217         this.loaded = true;
40218     }, 
40219     
40220     /**
40221      * Returns this panel's id
40222      * @return {String} 
40223      */
40224     getId : function(){
40225         return this.el.id;
40226     },
40227     
40228     /** 
40229      * Returns this panel's element - used by regiosn to add.
40230      * @return {Roo.Element} 
40231      */
40232     getEl : function(){
40233         return this.wrapEl || this.el;
40234     },
40235     
40236    
40237     
40238     adjustForComponents : function(width, height)
40239     {
40240         //Roo.log('adjustForComponents ');
40241         if(this.resizeEl != this.el){
40242             width -= this.el.getFrameWidth('lr');
40243             height -= this.el.getFrameWidth('tb');
40244         }
40245         if(this.toolbar){
40246             var te = this.toolbar.getEl();
40247             te.setWidth(width);
40248             height -= te.getHeight();
40249         }
40250         if(this.footer){
40251             var te = this.footer.getEl();
40252             te.setWidth(width);
40253             height -= te.getHeight();
40254         }
40255         
40256         
40257         if(this.adjustments){
40258             width += this.adjustments[0];
40259             height += this.adjustments[1];
40260         }
40261         return {"width": width, "height": height};
40262     },
40263     
40264     setSize : function(width, height){
40265         if(this.fitToFrame && !this.ignoreResize(width, height)){
40266             if(this.fitContainer && this.resizeEl != this.el){
40267                 this.el.setSize(width, height);
40268             }
40269             var size = this.adjustForComponents(width, height);
40270             if (this.iframe) {
40271                 this.iframeEl.setSize(width,height);
40272             }
40273             
40274             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40275             this.fireEvent('resize', this, size.width, size.height);
40276             
40277             
40278         }
40279     },
40280     
40281     /**
40282      * Returns this panel's title
40283      * @return {String} 
40284      */
40285     getTitle : function(){
40286         
40287         if (typeof(this.title) != 'object') {
40288             return this.title;
40289         }
40290         
40291         var t = '';
40292         for (var k in this.title) {
40293             if (!this.title.hasOwnProperty(k)) {
40294                 continue;
40295             }
40296             
40297             if (k.indexOf('-') >= 0) {
40298                 var s = k.split('-');
40299                 for (var i = 0; i<s.length; i++) {
40300                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40301                 }
40302             } else {
40303                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40304             }
40305         }
40306         return t;
40307     },
40308     
40309     /**
40310      * Set this panel's title
40311      * @param {String} title
40312      */
40313     setTitle : function(title){
40314         this.title = title;
40315         if(this.region){
40316             this.region.updatePanelTitle(this, title);
40317         }
40318     },
40319     
40320     /**
40321      * Returns true is this panel was configured to be closable
40322      * @return {Boolean} 
40323      */
40324     isClosable : function(){
40325         return this.closable;
40326     },
40327     
40328     beforeSlide : function(){
40329         this.el.clip();
40330         this.resizeEl.clip();
40331     },
40332     
40333     afterSlide : function(){
40334         this.el.unclip();
40335         this.resizeEl.unclip();
40336     },
40337     
40338     /**
40339      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40340      *   Will fail silently if the {@link #setUrl} method has not been called.
40341      *   This does not activate the panel, just updates its content.
40342      */
40343     refresh : function(){
40344         if(this.refreshDelegate){
40345            this.loaded = false;
40346            this.refreshDelegate();
40347         }
40348     },
40349     
40350     /**
40351      * Destroys this panel
40352      */
40353     destroy : function(){
40354         this.el.removeAllListeners();
40355         var tempEl = document.createElement("span");
40356         tempEl.appendChild(this.el.dom);
40357         tempEl.innerHTML = "";
40358         this.el.remove();
40359         this.el = null;
40360     },
40361     
40362     /**
40363      * form - if the content panel contains a form - this is a reference to it.
40364      * @type {Roo.form.Form}
40365      */
40366     form : false,
40367     /**
40368      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40369      *    This contains a reference to it.
40370      * @type {Roo.View}
40371      */
40372     view : false,
40373     
40374       /**
40375      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40376      * <pre><code>
40377
40378 layout.addxtype({
40379        xtype : 'Form',
40380        items: [ .... ]
40381    }
40382 );
40383
40384 </code></pre>
40385      * @param {Object} cfg Xtype definition of item to add.
40386      */
40387     
40388     
40389     getChildContainer: function () {
40390         return this.getEl();
40391     }
40392     
40393     
40394     /*
40395         var  ret = new Roo.factory(cfg);
40396         return ret;
40397         
40398         
40399         // add form..
40400         if (cfg.xtype.match(/^Form$/)) {
40401             
40402             var el;
40403             //if (this.footer) {
40404             //    el = this.footer.container.insertSibling(false, 'before');
40405             //} else {
40406                 el = this.el.createChild();
40407             //}
40408
40409             this.form = new  Roo.form.Form(cfg);
40410             
40411             
40412             if ( this.form.allItems.length) {
40413                 this.form.render(el.dom);
40414             }
40415             return this.form;
40416         }
40417         // should only have one of theses..
40418         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40419             // views.. should not be just added - used named prop 'view''
40420             
40421             cfg.el = this.el.appendChild(document.createElement("div"));
40422             // factory?
40423             
40424             var ret = new Roo.factory(cfg);
40425              
40426              ret.render && ret.render(false, ''); // render blank..
40427             this.view = ret;
40428             return ret;
40429         }
40430         return false;
40431     }
40432     \*/
40433 });
40434  
40435 /**
40436  * @class Roo.bootstrap.panel.Grid
40437  * @extends Roo.bootstrap.panel.Content
40438  * @constructor
40439  * Create a new GridPanel.
40440  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40441  * @param {Object} config A the config object
40442   
40443  */
40444
40445
40446
40447 Roo.bootstrap.panel.Grid = function(config)
40448 {
40449     
40450       
40451     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40452         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40453
40454     config.el = this.wrapper;
40455     //this.el = this.wrapper;
40456     
40457       if (config.container) {
40458         // ctor'ed from a Border/panel.grid
40459         
40460         
40461         this.wrapper.setStyle("overflow", "hidden");
40462         this.wrapper.addClass('roo-grid-container');
40463
40464     }
40465     
40466     
40467     if(config.toolbar){
40468         var tool_el = this.wrapper.createChild();    
40469         this.toolbar = Roo.factory(config.toolbar);
40470         var ti = [];
40471         if (config.toolbar.items) {
40472             ti = config.toolbar.items ;
40473             delete config.toolbar.items ;
40474         }
40475         
40476         var nitems = [];
40477         this.toolbar.render(tool_el);
40478         for(var i =0;i < ti.length;i++) {
40479           //  Roo.log(['add child', items[i]]);
40480             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40481         }
40482         this.toolbar.items = nitems;
40483         
40484         delete config.toolbar;
40485     }
40486     
40487     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40488     config.grid.scrollBody = true;;
40489     config.grid.monitorWindowResize = false; // turn off autosizing
40490     config.grid.autoHeight = false;
40491     config.grid.autoWidth = false;
40492     
40493     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40494     
40495     if (config.background) {
40496         // render grid on panel activation (if panel background)
40497         this.on('activate', function(gp) {
40498             if (!gp.grid.rendered) {
40499                 gp.grid.render(this.wrapper);
40500                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40501             }
40502         });
40503             
40504     } else {
40505         this.grid.render(this.wrapper);
40506         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40507
40508     }
40509     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40510     // ??? needed ??? config.el = this.wrapper;
40511     
40512     
40513     
40514   
40515     // xtype created footer. - not sure if will work as we normally have to render first..
40516     if (this.footer && !this.footer.el && this.footer.xtype) {
40517         
40518         var ctr = this.grid.getView().getFooterPanel(true);
40519         this.footer.dataSource = this.grid.dataSource;
40520         this.footer = Roo.factory(this.footer, Roo);
40521         this.footer.render(ctr);
40522         
40523     }
40524     
40525     
40526     
40527     
40528      
40529 };
40530
40531 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40532     getId : function(){
40533         return this.grid.id;
40534     },
40535     
40536     /**
40537      * Returns the grid for this panel
40538      * @return {Roo.bootstrap.Table} 
40539      */
40540     getGrid : function(){
40541         return this.grid;    
40542     },
40543     
40544     setSize : function(width, height){
40545         if(!this.ignoreResize(width, height)){
40546             var grid = this.grid;
40547             var size = this.adjustForComponents(width, height);
40548             // tfoot is not a footer?
40549           
40550             
40551             var gridel = grid.getGridEl();
40552             gridel.setSize(size.width, size.height);
40553             
40554             var tbd = grid.getGridEl().select('tbody', true).first();
40555             var thd = grid.getGridEl().select('thead',true).first();
40556             var tbf= grid.getGridEl().select('tfoot', true).first();
40557
40558             if (tbf) {
40559                 size.height -= tbf.getHeight();
40560             }
40561             if (thd) {
40562                 size.height -= thd.getHeight();
40563             }
40564             
40565             tbd.setSize(size.width, size.height );
40566             // this is for the account management tab -seems to work there.
40567             var thd = grid.getGridEl().select('thead',true).first();
40568             //if (tbd) {
40569             //    tbd.setSize(size.width, size.height - thd.getHeight());
40570             //}
40571              
40572             grid.autoSize();
40573         }
40574     },
40575      
40576     
40577     
40578     beforeSlide : function(){
40579         this.grid.getView().scroller.clip();
40580     },
40581     
40582     afterSlide : function(){
40583         this.grid.getView().scroller.unclip();
40584     },
40585     
40586     destroy : function(){
40587         this.grid.destroy();
40588         delete this.grid;
40589         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40590     }
40591 });
40592
40593 /**
40594  * @class Roo.bootstrap.panel.Nest
40595  * @extends Roo.bootstrap.panel.Content
40596  * @constructor
40597  * Create a new Panel, that can contain a layout.Border.
40598  * 
40599  * 
40600  * @param {Roo.BorderLayout} layout The layout for this panel
40601  * @param {String/Object} config A string to set only the title or a config object
40602  */
40603 Roo.bootstrap.panel.Nest = function(config)
40604 {
40605     // construct with only one argument..
40606     /* FIXME - implement nicer consturctors
40607     if (layout.layout) {
40608         config = layout;
40609         layout = config.layout;
40610         delete config.layout;
40611     }
40612     if (layout.xtype && !layout.getEl) {
40613         // then layout needs constructing..
40614         layout = Roo.factory(layout, Roo);
40615     }
40616     */
40617     
40618     config.el =  config.layout.getEl();
40619     
40620     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40621     
40622     config.layout.monitorWindowResize = false; // turn off autosizing
40623     this.layout = config.layout;
40624     this.layout.getEl().addClass("roo-layout-nested-layout");
40625     this.layout.parent = this;
40626     
40627     
40628     
40629     
40630 };
40631
40632 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40633
40634     setSize : function(width, height){
40635         if(!this.ignoreResize(width, height)){
40636             var size = this.adjustForComponents(width, height);
40637             var el = this.layout.getEl();
40638             if (size.height < 1) {
40639                 el.setWidth(size.width);   
40640             } else {
40641                 el.setSize(size.width, size.height);
40642             }
40643             var touch = el.dom.offsetWidth;
40644             this.layout.layout();
40645             // ie requires a double layout on the first pass
40646             if(Roo.isIE && !this.initialized){
40647                 this.initialized = true;
40648                 this.layout.layout();
40649             }
40650         }
40651     },
40652     
40653     // activate all subpanels if not currently active..
40654     
40655     setActiveState : function(active){
40656         this.active = active;
40657         this.setActiveClass(active);
40658         
40659         if(!active){
40660             this.fireEvent("deactivate", this);
40661             return;
40662         }
40663         
40664         this.fireEvent("activate", this);
40665         // not sure if this should happen before or after..
40666         if (!this.layout) {
40667             return; // should not happen..
40668         }
40669         var reg = false;
40670         for (var r in this.layout.regions) {
40671             reg = this.layout.getRegion(r);
40672             if (reg.getActivePanel()) {
40673                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40674                 reg.setActivePanel(reg.getActivePanel());
40675                 continue;
40676             }
40677             if (!reg.panels.length) {
40678                 continue;
40679             }
40680             reg.showPanel(reg.getPanel(0));
40681         }
40682         
40683         
40684         
40685         
40686     },
40687     
40688     /**
40689      * Returns the nested BorderLayout for this panel
40690      * @return {Roo.BorderLayout} 
40691      */
40692     getLayout : function(){
40693         return this.layout;
40694     },
40695     
40696      /**
40697      * Adds a xtype elements to the layout of the nested panel
40698      * <pre><code>
40699
40700 panel.addxtype({
40701        xtype : 'ContentPanel',
40702        region: 'west',
40703        items: [ .... ]
40704    }
40705 );
40706
40707 panel.addxtype({
40708         xtype : 'NestedLayoutPanel',
40709         region: 'west',
40710         layout: {
40711            center: { },
40712            west: { }   
40713         },
40714         items : [ ... list of content panels or nested layout panels.. ]
40715    }
40716 );
40717 </code></pre>
40718      * @param {Object} cfg Xtype definition of item to add.
40719      */
40720     addxtype : function(cfg) {
40721         return this.layout.addxtype(cfg);
40722     
40723     }
40724 });/*
40725  * Based on:
40726  * Ext JS Library 1.1.1
40727  * Copyright(c) 2006-2007, Ext JS, LLC.
40728  *
40729  * Originally Released Under LGPL - original licence link has changed is not relivant.
40730  *
40731  * Fork - LGPL
40732  * <script type="text/javascript">
40733  */
40734 /**
40735  * @class Roo.TabPanel
40736  * @extends Roo.util.Observable
40737  * A lightweight tab container.
40738  * <br><br>
40739  * Usage:
40740  * <pre><code>
40741 // basic tabs 1, built from existing content
40742 var tabs = new Roo.TabPanel("tabs1");
40743 tabs.addTab("script", "View Script");
40744 tabs.addTab("markup", "View Markup");
40745 tabs.activate("script");
40746
40747 // more advanced tabs, built from javascript
40748 var jtabs = new Roo.TabPanel("jtabs");
40749 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40750
40751 // set up the UpdateManager
40752 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40753 var updater = tab2.getUpdateManager();
40754 updater.setDefaultUrl("ajax1.htm");
40755 tab2.on('activate', updater.refresh, updater, true);
40756
40757 // Use setUrl for Ajax loading
40758 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40759 tab3.setUrl("ajax2.htm", null, true);
40760
40761 // Disabled tab
40762 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40763 tab4.disable();
40764
40765 jtabs.activate("jtabs-1");
40766  * </code></pre>
40767  * @constructor
40768  * Create a new TabPanel.
40769  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40770  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40771  */
40772 Roo.bootstrap.panel.Tabs = function(config){
40773     /**
40774     * The container element for this TabPanel.
40775     * @type Roo.Element
40776     */
40777     this.el = Roo.get(config.el);
40778     delete config.el;
40779     if(config){
40780         if(typeof config == "boolean"){
40781             this.tabPosition = config ? "bottom" : "top";
40782         }else{
40783             Roo.apply(this, config);
40784         }
40785     }
40786     
40787     if(this.tabPosition == "bottom"){
40788         // if tabs are at the bottom = create the body first.
40789         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40790         this.el.addClass("roo-tabs-bottom");
40791     }
40792     // next create the tabs holders
40793     
40794     if (this.tabPosition == "west"){
40795         
40796         var reg = this.region; // fake it..
40797         while (reg) {
40798             if (!reg.mgr.parent) {
40799                 break;
40800             }
40801             reg = reg.mgr.parent.region;
40802         }
40803         Roo.log("got nest?");
40804         Roo.log(reg);
40805         if (reg.mgr.getRegion('west')) {
40806             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40807             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40808             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40809             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40810             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40811         
40812             
40813         }
40814         
40815         
40816     } else {
40817      
40818         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40819         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40820         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40821         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40822     }
40823     
40824     
40825     if(Roo.isIE){
40826         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40827     }
40828     
40829     // finally - if tabs are at the top, then create the body last..
40830     if(this.tabPosition != "bottom"){
40831         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40832          * @type Roo.Element
40833          */
40834         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40835         this.el.addClass("roo-tabs-top");
40836     }
40837     this.items = [];
40838
40839     this.bodyEl.setStyle("position", "relative");
40840
40841     this.active = null;
40842     this.activateDelegate = this.activate.createDelegate(this);
40843
40844     this.addEvents({
40845         /**
40846          * @event tabchange
40847          * Fires when the active tab changes
40848          * @param {Roo.TabPanel} this
40849          * @param {Roo.TabPanelItem} activePanel The new active tab
40850          */
40851         "tabchange": true,
40852         /**
40853          * @event beforetabchange
40854          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40855          * @param {Roo.TabPanel} this
40856          * @param {Object} e Set cancel to true on this object to cancel the tab change
40857          * @param {Roo.TabPanelItem} tab The tab being changed to
40858          */
40859         "beforetabchange" : true
40860     });
40861
40862     Roo.EventManager.onWindowResize(this.onResize, this);
40863     this.cpad = this.el.getPadding("lr");
40864     this.hiddenCount = 0;
40865
40866
40867     // toolbar on the tabbar support...
40868     if (this.toolbar) {
40869         alert("no toolbar support yet");
40870         this.toolbar  = false;
40871         /*
40872         var tcfg = this.toolbar;
40873         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40874         this.toolbar = new Roo.Toolbar(tcfg);
40875         if (Roo.isSafari) {
40876             var tbl = tcfg.container.child('table', true);
40877             tbl.setAttribute('width', '100%');
40878         }
40879         */
40880         
40881     }
40882    
40883
40884
40885     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40886 };
40887
40888 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40889     /*
40890      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40891      */
40892     tabPosition : "top",
40893     /*
40894      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40895      */
40896     currentTabWidth : 0,
40897     /*
40898      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40899      */
40900     minTabWidth : 40,
40901     /*
40902      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40903      */
40904     maxTabWidth : 250,
40905     /*
40906      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40907      */
40908     preferredTabWidth : 175,
40909     /*
40910      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40911      */
40912     resizeTabs : false,
40913     /*
40914      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40915      */
40916     monitorResize : true,
40917     /*
40918      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40919      */
40920     toolbar : false,  // set by caller..
40921     
40922     region : false, /// set by caller
40923     
40924     disableTooltips : true, // not used yet...
40925
40926     /**
40927      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40928      * @param {String} id The id of the div to use <b>or create</b>
40929      * @param {String} text The text for the tab
40930      * @param {String} content (optional) Content to put in the TabPanelItem body
40931      * @param {Boolean} closable (optional) True to create a close icon on the tab
40932      * @return {Roo.TabPanelItem} The created TabPanelItem
40933      */
40934     addTab : function(id, text, content, closable, tpl)
40935     {
40936         var item = new Roo.bootstrap.panel.TabItem({
40937             panel: this,
40938             id : id,
40939             text : text,
40940             closable : closable,
40941             tpl : tpl
40942         });
40943         this.addTabItem(item);
40944         if(content){
40945             item.setContent(content);
40946         }
40947         return item;
40948     },
40949
40950     /**
40951      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40952      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40953      * @return {Roo.TabPanelItem}
40954      */
40955     getTab : function(id){
40956         return this.items[id];
40957     },
40958
40959     /**
40960      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40961      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40962      */
40963     hideTab : function(id){
40964         var t = this.items[id];
40965         if(!t.isHidden()){
40966            t.setHidden(true);
40967            this.hiddenCount++;
40968            this.autoSizeTabs();
40969         }
40970     },
40971
40972     /**
40973      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40974      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40975      */
40976     unhideTab : function(id){
40977         var t = this.items[id];
40978         if(t.isHidden()){
40979            t.setHidden(false);
40980            this.hiddenCount--;
40981            this.autoSizeTabs();
40982         }
40983     },
40984
40985     /**
40986      * Adds an existing {@link Roo.TabPanelItem}.
40987      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40988      */
40989     addTabItem : function(item)
40990     {
40991         this.items[item.id] = item;
40992         this.items.push(item);
40993         this.autoSizeTabs();
40994       //  if(this.resizeTabs){
40995     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40996   //         this.autoSizeTabs();
40997 //        }else{
40998 //            item.autoSize();
40999        // }
41000     },
41001
41002     /**
41003      * Removes a {@link Roo.TabPanelItem}.
41004      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41005      */
41006     removeTab : function(id){
41007         var items = this.items;
41008         var tab = items[id];
41009         if(!tab) { return; }
41010         var index = items.indexOf(tab);
41011         if(this.active == tab && items.length > 1){
41012             var newTab = this.getNextAvailable(index);
41013             if(newTab) {
41014                 newTab.activate();
41015             }
41016         }
41017         this.stripEl.dom.removeChild(tab.pnode.dom);
41018         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41019             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41020         }
41021         items.splice(index, 1);
41022         delete this.items[tab.id];
41023         tab.fireEvent("close", tab);
41024         tab.purgeListeners();
41025         this.autoSizeTabs();
41026     },
41027
41028     getNextAvailable : function(start){
41029         var items = this.items;
41030         var index = start;
41031         // look for a next tab that will slide over to
41032         // replace the one being removed
41033         while(index < items.length){
41034             var item = items[++index];
41035             if(item && !item.isHidden()){
41036                 return item;
41037             }
41038         }
41039         // if one isn't found select the previous tab (on the left)
41040         index = start;
41041         while(index >= 0){
41042             var item = items[--index];
41043             if(item && !item.isHidden()){
41044                 return item;
41045             }
41046         }
41047         return null;
41048     },
41049
41050     /**
41051      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41052      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41053      */
41054     disableTab : function(id){
41055         var tab = this.items[id];
41056         if(tab && this.active != tab){
41057             tab.disable();
41058         }
41059     },
41060
41061     /**
41062      * Enables a {@link Roo.TabPanelItem} that is disabled.
41063      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41064      */
41065     enableTab : function(id){
41066         var tab = this.items[id];
41067         tab.enable();
41068     },
41069
41070     /**
41071      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41072      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41073      * @return {Roo.TabPanelItem} The TabPanelItem.
41074      */
41075     activate : function(id)
41076     {
41077         //Roo.log('activite:'  + id);
41078         
41079         var tab = this.items[id];
41080         if(!tab){
41081             return null;
41082         }
41083         if(tab == this.active || tab.disabled){
41084             return tab;
41085         }
41086         var e = {};
41087         this.fireEvent("beforetabchange", this, e, tab);
41088         if(e.cancel !== true && !tab.disabled){
41089             if(this.active){
41090                 this.active.hide();
41091             }
41092             this.active = this.items[id];
41093             this.active.show();
41094             this.fireEvent("tabchange", this, this.active);
41095         }
41096         return tab;
41097     },
41098
41099     /**
41100      * Gets the active {@link Roo.TabPanelItem}.
41101      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41102      */
41103     getActiveTab : function(){
41104         return this.active;
41105     },
41106
41107     /**
41108      * Updates the tab body element to fit the height of the container element
41109      * for overflow scrolling
41110      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41111      */
41112     syncHeight : function(targetHeight){
41113         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41114         var bm = this.bodyEl.getMargins();
41115         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41116         this.bodyEl.setHeight(newHeight);
41117         return newHeight;
41118     },
41119
41120     onResize : function(){
41121         if(this.monitorResize){
41122             this.autoSizeTabs();
41123         }
41124     },
41125
41126     /**
41127      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41128      */
41129     beginUpdate : function(){
41130         this.updating = true;
41131     },
41132
41133     /**
41134      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41135      */
41136     endUpdate : function(){
41137         this.updating = false;
41138         this.autoSizeTabs();
41139     },
41140
41141     /**
41142      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41143      */
41144     autoSizeTabs : function()
41145     {
41146         var count = this.items.length;
41147         var vcount = count - this.hiddenCount;
41148         
41149         if (vcount < 2) {
41150             this.stripEl.hide();
41151         } else {
41152             this.stripEl.show();
41153         }
41154         
41155         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41156             return;
41157         }
41158         
41159         
41160         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41161         var availWidth = Math.floor(w / vcount);
41162         var b = this.stripBody;
41163         if(b.getWidth() > w){
41164             var tabs = this.items;
41165             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41166             if(availWidth < this.minTabWidth){
41167                 /*if(!this.sleft){    // incomplete scrolling code
41168                     this.createScrollButtons();
41169                 }
41170                 this.showScroll();
41171                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41172             }
41173         }else{
41174             if(this.currentTabWidth < this.preferredTabWidth){
41175                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41176             }
41177         }
41178     },
41179
41180     /**
41181      * Returns the number of tabs in this TabPanel.
41182      * @return {Number}
41183      */
41184      getCount : function(){
41185          return this.items.length;
41186      },
41187
41188     /**
41189      * Resizes all the tabs to the passed width
41190      * @param {Number} The new width
41191      */
41192     setTabWidth : function(width){
41193         this.currentTabWidth = width;
41194         for(var i = 0, len = this.items.length; i < len; i++) {
41195                 if(!this.items[i].isHidden()) {
41196                 this.items[i].setWidth(width);
41197             }
41198         }
41199     },
41200
41201     /**
41202      * Destroys this TabPanel
41203      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41204      */
41205     destroy : function(removeEl){
41206         Roo.EventManager.removeResizeListener(this.onResize, this);
41207         for(var i = 0, len = this.items.length; i < len; i++){
41208             this.items[i].purgeListeners();
41209         }
41210         if(removeEl === true){
41211             this.el.update("");
41212             this.el.remove();
41213         }
41214     },
41215     
41216     createStrip : function(container)
41217     {
41218         var strip = document.createElement("nav");
41219         strip.className = Roo.bootstrap.version == 4 ?
41220             "navbar-light bg-light" : 
41221             "navbar navbar-default"; //"x-tabs-wrap";
41222         container.appendChild(strip);
41223         return strip;
41224     },
41225     
41226     createStripList : function(strip)
41227     {
41228         // div wrapper for retard IE
41229         // returns the "tr" element.
41230         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41231         //'<div class="x-tabs-strip-wrap">'+
41232           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41233           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41234         return strip.firstChild; //.firstChild.firstChild.firstChild;
41235     },
41236     createBody : function(container)
41237     {
41238         var body = document.createElement("div");
41239         Roo.id(body, "tab-body");
41240         //Roo.fly(body).addClass("x-tabs-body");
41241         Roo.fly(body).addClass("tab-content");
41242         container.appendChild(body);
41243         return body;
41244     },
41245     createItemBody :function(bodyEl, id){
41246         var body = Roo.getDom(id);
41247         if(!body){
41248             body = document.createElement("div");
41249             body.id = id;
41250         }
41251         //Roo.fly(body).addClass("x-tabs-item-body");
41252         Roo.fly(body).addClass("tab-pane");
41253          bodyEl.insertBefore(body, bodyEl.firstChild);
41254         return body;
41255     },
41256     /** @private */
41257     createStripElements :  function(stripEl, text, closable, tpl)
41258     {
41259         var td = document.createElement("li"); // was td..
41260         td.className = 'nav-item';
41261         
41262         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41263         
41264         
41265         stripEl.appendChild(td);
41266         /*if(closable){
41267             td.className = "x-tabs-closable";
41268             if(!this.closeTpl){
41269                 this.closeTpl = new Roo.Template(
41270                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41271                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41272                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41273                 );
41274             }
41275             var el = this.closeTpl.overwrite(td, {"text": text});
41276             var close = el.getElementsByTagName("div")[0];
41277             var inner = el.getElementsByTagName("em")[0];
41278             return {"el": el, "close": close, "inner": inner};
41279         } else {
41280         */
41281         // not sure what this is..
41282 //            if(!this.tabTpl){
41283                 //this.tabTpl = new Roo.Template(
41284                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41285                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41286                 //);
41287 //                this.tabTpl = new Roo.Template(
41288 //                   '<a href="#">' +
41289 //                   '<span unselectable="on"' +
41290 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41291 //                            ' >{text}</span></a>'
41292 //                );
41293 //                
41294 //            }
41295
41296
41297             var template = tpl || this.tabTpl || false;
41298             
41299             if(!template){
41300                 template =  new Roo.Template(
41301                         Roo.bootstrap.version == 4 ? 
41302                             (
41303                                 '<a class="nav-link" href="#" unselectable="on"' +
41304                                      (this.disableTooltips ? '' : ' title="{text}"') +
41305                                      ' >{text}</a>'
41306                             ) : (
41307                                 '<a class="nav-link" href="#">' +
41308                                 '<span unselectable="on"' +
41309                                          (this.disableTooltips ? '' : ' title="{text}"') +
41310                                     ' >{text}</span></a>'
41311                             )
41312                 );
41313             }
41314             
41315             switch (typeof(template)) {
41316                 case 'object' :
41317                     break;
41318                 case 'string' :
41319                     template = new Roo.Template(template);
41320                     break;
41321                 default :
41322                     break;
41323             }
41324             
41325             var el = template.overwrite(td, {"text": text});
41326             
41327             var inner = el.getElementsByTagName("span")[0];
41328             
41329             return {"el": el, "inner": inner};
41330             
41331     }
41332         
41333     
41334 });
41335
41336 /**
41337  * @class Roo.TabPanelItem
41338  * @extends Roo.util.Observable
41339  * Represents an individual item (tab plus body) in a TabPanel.
41340  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41341  * @param {String} id The id of this TabPanelItem
41342  * @param {String} text The text for the tab of this TabPanelItem
41343  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41344  */
41345 Roo.bootstrap.panel.TabItem = function(config){
41346     /**
41347      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41348      * @type Roo.TabPanel
41349      */
41350     this.tabPanel = config.panel;
41351     /**
41352      * The id for this TabPanelItem
41353      * @type String
41354      */
41355     this.id = config.id;
41356     /** @private */
41357     this.disabled = false;
41358     /** @private */
41359     this.text = config.text;
41360     /** @private */
41361     this.loaded = false;
41362     this.closable = config.closable;
41363
41364     /**
41365      * The body element for this TabPanelItem.
41366      * @type Roo.Element
41367      */
41368     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41369     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41370     this.bodyEl.setStyle("display", "block");
41371     this.bodyEl.setStyle("zoom", "1");
41372     //this.hideAction();
41373
41374     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41375     /** @private */
41376     this.el = Roo.get(els.el);
41377     this.inner = Roo.get(els.inner, true);
41378      this.textEl = Roo.bootstrap.version == 4 ?
41379         this.el : Roo.get(this.el.dom.firstChild, true);
41380
41381     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41382     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41383
41384     
41385 //    this.el.on("mousedown", this.onTabMouseDown, this);
41386     this.el.on("click", this.onTabClick, this);
41387     /** @private */
41388     if(config.closable){
41389         var c = Roo.get(els.close, true);
41390         c.dom.title = this.closeText;
41391         c.addClassOnOver("close-over");
41392         c.on("click", this.closeClick, this);
41393      }
41394
41395     this.addEvents({
41396          /**
41397          * @event activate
41398          * Fires when this tab becomes the active tab.
41399          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41400          * @param {Roo.TabPanelItem} this
41401          */
41402         "activate": true,
41403         /**
41404          * @event beforeclose
41405          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41406          * @param {Roo.TabPanelItem} this
41407          * @param {Object} e Set cancel to true on this object to cancel the close.
41408          */
41409         "beforeclose": true,
41410         /**
41411          * @event close
41412          * Fires when this tab is closed.
41413          * @param {Roo.TabPanelItem} this
41414          */
41415          "close": true,
41416         /**
41417          * @event deactivate
41418          * Fires when this tab is no longer the active tab.
41419          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41420          * @param {Roo.TabPanelItem} this
41421          */
41422          "deactivate" : true
41423     });
41424     this.hidden = false;
41425
41426     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41427 };
41428
41429 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41430            {
41431     purgeListeners : function(){
41432        Roo.util.Observable.prototype.purgeListeners.call(this);
41433        this.el.removeAllListeners();
41434     },
41435     /**
41436      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41437      */
41438     show : function(){
41439         this.status_node.addClass("active");
41440         this.showAction();
41441         if(Roo.isOpera){
41442             this.tabPanel.stripWrap.repaint();
41443         }
41444         this.fireEvent("activate", this.tabPanel, this);
41445     },
41446
41447     /**
41448      * Returns true if this tab is the active tab.
41449      * @return {Boolean}
41450      */
41451     isActive : function(){
41452         return this.tabPanel.getActiveTab() == this;
41453     },
41454
41455     /**
41456      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41457      */
41458     hide : function(){
41459         this.status_node.removeClass("active");
41460         this.hideAction();
41461         this.fireEvent("deactivate", this.tabPanel, this);
41462     },
41463
41464     hideAction : function(){
41465         this.bodyEl.hide();
41466         this.bodyEl.setStyle("position", "absolute");
41467         this.bodyEl.setLeft("-20000px");
41468         this.bodyEl.setTop("-20000px");
41469     },
41470
41471     showAction : function(){
41472         this.bodyEl.setStyle("position", "relative");
41473         this.bodyEl.setTop("");
41474         this.bodyEl.setLeft("");
41475         this.bodyEl.show();
41476     },
41477
41478     /**
41479      * Set the tooltip for the tab.
41480      * @param {String} tooltip The tab's tooltip
41481      */
41482     setTooltip : function(text){
41483         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41484             this.textEl.dom.qtip = text;
41485             this.textEl.dom.removeAttribute('title');
41486         }else{
41487             this.textEl.dom.title = text;
41488         }
41489     },
41490
41491     onTabClick : function(e){
41492         e.preventDefault();
41493         this.tabPanel.activate(this.id);
41494     },
41495
41496     onTabMouseDown : function(e){
41497         e.preventDefault();
41498         this.tabPanel.activate(this.id);
41499     },
41500 /*
41501     getWidth : function(){
41502         return this.inner.getWidth();
41503     },
41504
41505     setWidth : function(width){
41506         var iwidth = width - this.linode.getPadding("lr");
41507         this.inner.setWidth(iwidth);
41508         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41509         this.linode.setWidth(width);
41510     },
41511 */
41512     /**
41513      * Show or hide the tab
41514      * @param {Boolean} hidden True to hide or false to show.
41515      */
41516     setHidden : function(hidden){
41517         this.hidden = hidden;
41518         this.linode.setStyle("display", hidden ? "none" : "");
41519     },
41520
41521     /**
41522      * Returns true if this tab is "hidden"
41523      * @return {Boolean}
41524      */
41525     isHidden : function(){
41526         return this.hidden;
41527     },
41528
41529     /**
41530      * Returns the text for this tab
41531      * @return {String}
41532      */
41533     getText : function(){
41534         return this.text;
41535     },
41536     /*
41537     autoSize : function(){
41538         //this.el.beginMeasure();
41539         this.textEl.setWidth(1);
41540         /*
41541          *  #2804 [new] Tabs in Roojs
41542          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41543          */
41544         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41545         //this.el.endMeasure();
41546     //},
41547
41548     /**
41549      * Sets the text for the tab (Note: this also sets the tooltip text)
41550      * @param {String} text The tab's text and tooltip
41551      */
41552     setText : function(text){
41553         this.text = text;
41554         this.textEl.update(text);
41555         this.setTooltip(text);
41556         //if(!this.tabPanel.resizeTabs){
41557         //    this.autoSize();
41558         //}
41559     },
41560     /**
41561      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41562      */
41563     activate : function(){
41564         this.tabPanel.activate(this.id);
41565     },
41566
41567     /**
41568      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41569      */
41570     disable : function(){
41571         if(this.tabPanel.active != this){
41572             this.disabled = true;
41573             this.status_node.addClass("disabled");
41574         }
41575     },
41576
41577     /**
41578      * Enables this TabPanelItem if it was previously disabled.
41579      */
41580     enable : function(){
41581         this.disabled = false;
41582         this.status_node.removeClass("disabled");
41583     },
41584
41585     /**
41586      * Sets the content for this TabPanelItem.
41587      * @param {String} content The content
41588      * @param {Boolean} loadScripts true to look for and load scripts
41589      */
41590     setContent : function(content, loadScripts){
41591         this.bodyEl.update(content, loadScripts);
41592     },
41593
41594     /**
41595      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41596      * @return {Roo.UpdateManager} The UpdateManager
41597      */
41598     getUpdateManager : function(){
41599         return this.bodyEl.getUpdateManager();
41600     },
41601
41602     /**
41603      * Set a URL to be used to load the content for this TabPanelItem.
41604      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41605      * @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)
41606      * @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)
41607      * @return {Roo.UpdateManager} The UpdateManager
41608      */
41609     setUrl : function(url, params, loadOnce){
41610         if(this.refreshDelegate){
41611             this.un('activate', this.refreshDelegate);
41612         }
41613         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41614         this.on("activate", this.refreshDelegate);
41615         return this.bodyEl.getUpdateManager();
41616     },
41617
41618     /** @private */
41619     _handleRefresh : function(url, params, loadOnce){
41620         if(!loadOnce || !this.loaded){
41621             var updater = this.bodyEl.getUpdateManager();
41622             updater.update(url, params, this._setLoaded.createDelegate(this));
41623         }
41624     },
41625
41626     /**
41627      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41628      *   Will fail silently if the setUrl method has not been called.
41629      *   This does not activate the panel, just updates its content.
41630      */
41631     refresh : function(){
41632         if(this.refreshDelegate){
41633            this.loaded = false;
41634            this.refreshDelegate();
41635         }
41636     },
41637
41638     /** @private */
41639     _setLoaded : function(){
41640         this.loaded = true;
41641     },
41642
41643     /** @private */
41644     closeClick : function(e){
41645         var o = {};
41646         e.stopEvent();
41647         this.fireEvent("beforeclose", this, o);
41648         if(o.cancel !== true){
41649             this.tabPanel.removeTab(this.id);
41650         }
41651     },
41652     /**
41653      * The text displayed in the tooltip for the close icon.
41654      * @type String
41655      */
41656     closeText : "Close this tab"
41657 });
41658 /**
41659 *    This script refer to:
41660 *    Title: International Telephone Input
41661 *    Author: Jack O'Connor
41662 *    Code version:  v12.1.12
41663 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41664 **/
41665
41666 Roo.bootstrap.PhoneInputData = function() {
41667     var d = [
41668       [
41669         "Afghanistan (‫افغانستان‬‎)",
41670         "af",
41671         "93"
41672       ],
41673       [
41674         "Albania (Shqipëri)",
41675         "al",
41676         "355"
41677       ],
41678       [
41679         "Algeria (‫الجزائر‬‎)",
41680         "dz",
41681         "213"
41682       ],
41683       [
41684         "American Samoa",
41685         "as",
41686         "1684"
41687       ],
41688       [
41689         "Andorra",
41690         "ad",
41691         "376"
41692       ],
41693       [
41694         "Angola",
41695         "ao",
41696         "244"
41697       ],
41698       [
41699         "Anguilla",
41700         "ai",
41701         "1264"
41702       ],
41703       [
41704         "Antigua and Barbuda",
41705         "ag",
41706         "1268"
41707       ],
41708       [
41709         "Argentina",
41710         "ar",
41711         "54"
41712       ],
41713       [
41714         "Armenia (Հայաստան)",
41715         "am",
41716         "374"
41717       ],
41718       [
41719         "Aruba",
41720         "aw",
41721         "297"
41722       ],
41723       [
41724         "Australia",
41725         "au",
41726         "61",
41727         0
41728       ],
41729       [
41730         "Austria (Österreich)",
41731         "at",
41732         "43"
41733       ],
41734       [
41735         "Azerbaijan (Azərbaycan)",
41736         "az",
41737         "994"
41738       ],
41739       [
41740         "Bahamas",
41741         "bs",
41742         "1242"
41743       ],
41744       [
41745         "Bahrain (‫البحرين‬‎)",
41746         "bh",
41747         "973"
41748       ],
41749       [
41750         "Bangladesh (বাংলাদেশ)",
41751         "bd",
41752         "880"
41753       ],
41754       [
41755         "Barbados",
41756         "bb",
41757         "1246"
41758       ],
41759       [
41760         "Belarus (Беларусь)",
41761         "by",
41762         "375"
41763       ],
41764       [
41765         "Belgium (België)",
41766         "be",
41767         "32"
41768       ],
41769       [
41770         "Belize",
41771         "bz",
41772         "501"
41773       ],
41774       [
41775         "Benin (Bénin)",
41776         "bj",
41777         "229"
41778       ],
41779       [
41780         "Bermuda",
41781         "bm",
41782         "1441"
41783       ],
41784       [
41785         "Bhutan (འབྲུག)",
41786         "bt",
41787         "975"
41788       ],
41789       [
41790         "Bolivia",
41791         "bo",
41792         "591"
41793       ],
41794       [
41795         "Bosnia and Herzegovina (Босна и Херцеговина)",
41796         "ba",
41797         "387"
41798       ],
41799       [
41800         "Botswana",
41801         "bw",
41802         "267"
41803       ],
41804       [
41805         "Brazil (Brasil)",
41806         "br",
41807         "55"
41808       ],
41809       [
41810         "British Indian Ocean Territory",
41811         "io",
41812         "246"
41813       ],
41814       [
41815         "British Virgin Islands",
41816         "vg",
41817         "1284"
41818       ],
41819       [
41820         "Brunei",
41821         "bn",
41822         "673"
41823       ],
41824       [
41825         "Bulgaria (България)",
41826         "bg",
41827         "359"
41828       ],
41829       [
41830         "Burkina Faso",
41831         "bf",
41832         "226"
41833       ],
41834       [
41835         "Burundi (Uburundi)",
41836         "bi",
41837         "257"
41838       ],
41839       [
41840         "Cambodia (កម្ពុជា)",
41841         "kh",
41842         "855"
41843       ],
41844       [
41845         "Cameroon (Cameroun)",
41846         "cm",
41847         "237"
41848       ],
41849       [
41850         "Canada",
41851         "ca",
41852         "1",
41853         1,
41854         ["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"]
41855       ],
41856       [
41857         "Cape Verde (Kabu Verdi)",
41858         "cv",
41859         "238"
41860       ],
41861       [
41862         "Caribbean Netherlands",
41863         "bq",
41864         "599",
41865         1
41866       ],
41867       [
41868         "Cayman Islands",
41869         "ky",
41870         "1345"
41871       ],
41872       [
41873         "Central African Republic (République centrafricaine)",
41874         "cf",
41875         "236"
41876       ],
41877       [
41878         "Chad (Tchad)",
41879         "td",
41880         "235"
41881       ],
41882       [
41883         "Chile",
41884         "cl",
41885         "56"
41886       ],
41887       [
41888         "China (中国)",
41889         "cn",
41890         "86"
41891       ],
41892       [
41893         "Christmas Island",
41894         "cx",
41895         "61",
41896         2
41897       ],
41898       [
41899         "Cocos (Keeling) Islands",
41900         "cc",
41901         "61",
41902         1
41903       ],
41904       [
41905         "Colombia",
41906         "co",
41907         "57"
41908       ],
41909       [
41910         "Comoros (‫جزر القمر‬‎)",
41911         "km",
41912         "269"
41913       ],
41914       [
41915         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41916         "cd",
41917         "243"
41918       ],
41919       [
41920         "Congo (Republic) (Congo-Brazzaville)",
41921         "cg",
41922         "242"
41923       ],
41924       [
41925         "Cook Islands",
41926         "ck",
41927         "682"
41928       ],
41929       [
41930         "Costa Rica",
41931         "cr",
41932         "506"
41933       ],
41934       [
41935         "Côte d’Ivoire",
41936         "ci",
41937         "225"
41938       ],
41939       [
41940         "Croatia (Hrvatska)",
41941         "hr",
41942         "385"
41943       ],
41944       [
41945         "Cuba",
41946         "cu",
41947         "53"
41948       ],
41949       [
41950         "Curaçao",
41951         "cw",
41952         "599",
41953         0
41954       ],
41955       [
41956         "Cyprus (Κύπρος)",
41957         "cy",
41958         "357"
41959       ],
41960       [
41961         "Czech Republic (Česká republika)",
41962         "cz",
41963         "420"
41964       ],
41965       [
41966         "Denmark (Danmark)",
41967         "dk",
41968         "45"
41969       ],
41970       [
41971         "Djibouti",
41972         "dj",
41973         "253"
41974       ],
41975       [
41976         "Dominica",
41977         "dm",
41978         "1767"
41979       ],
41980       [
41981         "Dominican Republic (República Dominicana)",
41982         "do",
41983         "1",
41984         2,
41985         ["809", "829", "849"]
41986       ],
41987       [
41988         "Ecuador",
41989         "ec",
41990         "593"
41991       ],
41992       [
41993         "Egypt (‫مصر‬‎)",
41994         "eg",
41995         "20"
41996       ],
41997       [
41998         "El Salvador",
41999         "sv",
42000         "503"
42001       ],
42002       [
42003         "Equatorial Guinea (Guinea Ecuatorial)",
42004         "gq",
42005         "240"
42006       ],
42007       [
42008         "Eritrea",
42009         "er",
42010         "291"
42011       ],
42012       [
42013         "Estonia (Eesti)",
42014         "ee",
42015         "372"
42016       ],
42017       [
42018         "Ethiopia",
42019         "et",
42020         "251"
42021       ],
42022       [
42023         "Falkland Islands (Islas Malvinas)",
42024         "fk",
42025         "500"
42026       ],
42027       [
42028         "Faroe Islands (Føroyar)",
42029         "fo",
42030         "298"
42031       ],
42032       [
42033         "Fiji",
42034         "fj",
42035         "679"
42036       ],
42037       [
42038         "Finland (Suomi)",
42039         "fi",
42040         "358",
42041         0
42042       ],
42043       [
42044         "France",
42045         "fr",
42046         "33"
42047       ],
42048       [
42049         "French Guiana (Guyane française)",
42050         "gf",
42051         "594"
42052       ],
42053       [
42054         "French Polynesia (Polynésie française)",
42055         "pf",
42056         "689"
42057       ],
42058       [
42059         "Gabon",
42060         "ga",
42061         "241"
42062       ],
42063       [
42064         "Gambia",
42065         "gm",
42066         "220"
42067       ],
42068       [
42069         "Georgia (საქართველო)",
42070         "ge",
42071         "995"
42072       ],
42073       [
42074         "Germany (Deutschland)",
42075         "de",
42076         "49"
42077       ],
42078       [
42079         "Ghana (Gaana)",
42080         "gh",
42081         "233"
42082       ],
42083       [
42084         "Gibraltar",
42085         "gi",
42086         "350"
42087       ],
42088       [
42089         "Greece (Ελλάδα)",
42090         "gr",
42091         "30"
42092       ],
42093       [
42094         "Greenland (Kalaallit Nunaat)",
42095         "gl",
42096         "299"
42097       ],
42098       [
42099         "Grenada",
42100         "gd",
42101         "1473"
42102       ],
42103       [
42104         "Guadeloupe",
42105         "gp",
42106         "590",
42107         0
42108       ],
42109       [
42110         "Guam",
42111         "gu",
42112         "1671"
42113       ],
42114       [
42115         "Guatemala",
42116         "gt",
42117         "502"
42118       ],
42119       [
42120         "Guernsey",
42121         "gg",
42122         "44",
42123         1
42124       ],
42125       [
42126         "Guinea (Guinée)",
42127         "gn",
42128         "224"
42129       ],
42130       [
42131         "Guinea-Bissau (Guiné Bissau)",
42132         "gw",
42133         "245"
42134       ],
42135       [
42136         "Guyana",
42137         "gy",
42138         "592"
42139       ],
42140       [
42141         "Haiti",
42142         "ht",
42143         "509"
42144       ],
42145       [
42146         "Honduras",
42147         "hn",
42148         "504"
42149       ],
42150       [
42151         "Hong Kong (香港)",
42152         "hk",
42153         "852"
42154       ],
42155       [
42156         "Hungary (Magyarország)",
42157         "hu",
42158         "36"
42159       ],
42160       [
42161         "Iceland (Ísland)",
42162         "is",
42163         "354"
42164       ],
42165       [
42166         "India (भारत)",
42167         "in",
42168         "91"
42169       ],
42170       [
42171         "Indonesia",
42172         "id",
42173         "62"
42174       ],
42175       [
42176         "Iran (‫ایران‬‎)",
42177         "ir",
42178         "98"
42179       ],
42180       [
42181         "Iraq (‫العراق‬‎)",
42182         "iq",
42183         "964"
42184       ],
42185       [
42186         "Ireland",
42187         "ie",
42188         "353"
42189       ],
42190       [
42191         "Isle of Man",
42192         "im",
42193         "44",
42194         2
42195       ],
42196       [
42197         "Israel (‫ישראל‬‎)",
42198         "il",
42199         "972"
42200       ],
42201       [
42202         "Italy (Italia)",
42203         "it",
42204         "39",
42205         0
42206       ],
42207       [
42208         "Jamaica",
42209         "jm",
42210         "1876"
42211       ],
42212       [
42213         "Japan (日本)",
42214         "jp",
42215         "81"
42216       ],
42217       [
42218         "Jersey",
42219         "je",
42220         "44",
42221         3
42222       ],
42223       [
42224         "Jordan (‫الأردن‬‎)",
42225         "jo",
42226         "962"
42227       ],
42228       [
42229         "Kazakhstan (Казахстан)",
42230         "kz",
42231         "7",
42232         1
42233       ],
42234       [
42235         "Kenya",
42236         "ke",
42237         "254"
42238       ],
42239       [
42240         "Kiribati",
42241         "ki",
42242         "686"
42243       ],
42244       [
42245         "Kosovo",
42246         "xk",
42247         "383"
42248       ],
42249       [
42250         "Kuwait (‫الكويت‬‎)",
42251         "kw",
42252         "965"
42253       ],
42254       [
42255         "Kyrgyzstan (Кыргызстан)",
42256         "kg",
42257         "996"
42258       ],
42259       [
42260         "Laos (ລາວ)",
42261         "la",
42262         "856"
42263       ],
42264       [
42265         "Latvia (Latvija)",
42266         "lv",
42267         "371"
42268       ],
42269       [
42270         "Lebanon (‫لبنان‬‎)",
42271         "lb",
42272         "961"
42273       ],
42274       [
42275         "Lesotho",
42276         "ls",
42277         "266"
42278       ],
42279       [
42280         "Liberia",
42281         "lr",
42282         "231"
42283       ],
42284       [
42285         "Libya (‫ليبيا‬‎)",
42286         "ly",
42287         "218"
42288       ],
42289       [
42290         "Liechtenstein",
42291         "li",
42292         "423"
42293       ],
42294       [
42295         "Lithuania (Lietuva)",
42296         "lt",
42297         "370"
42298       ],
42299       [
42300         "Luxembourg",
42301         "lu",
42302         "352"
42303       ],
42304       [
42305         "Macau (澳門)",
42306         "mo",
42307         "853"
42308       ],
42309       [
42310         "Macedonia (FYROM) (Македонија)",
42311         "mk",
42312         "389"
42313       ],
42314       [
42315         "Madagascar (Madagasikara)",
42316         "mg",
42317         "261"
42318       ],
42319       [
42320         "Malawi",
42321         "mw",
42322         "265"
42323       ],
42324       [
42325         "Malaysia",
42326         "my",
42327         "60"
42328       ],
42329       [
42330         "Maldives",
42331         "mv",
42332         "960"
42333       ],
42334       [
42335         "Mali",
42336         "ml",
42337         "223"
42338       ],
42339       [
42340         "Malta",
42341         "mt",
42342         "356"
42343       ],
42344       [
42345         "Marshall Islands",
42346         "mh",
42347         "692"
42348       ],
42349       [
42350         "Martinique",
42351         "mq",
42352         "596"
42353       ],
42354       [
42355         "Mauritania (‫موريتانيا‬‎)",
42356         "mr",
42357         "222"
42358       ],
42359       [
42360         "Mauritius (Moris)",
42361         "mu",
42362         "230"
42363       ],
42364       [
42365         "Mayotte",
42366         "yt",
42367         "262",
42368         1
42369       ],
42370       [
42371         "Mexico (México)",
42372         "mx",
42373         "52"
42374       ],
42375       [
42376         "Micronesia",
42377         "fm",
42378         "691"
42379       ],
42380       [
42381         "Moldova (Republica Moldova)",
42382         "md",
42383         "373"
42384       ],
42385       [
42386         "Monaco",
42387         "mc",
42388         "377"
42389       ],
42390       [
42391         "Mongolia (Монгол)",
42392         "mn",
42393         "976"
42394       ],
42395       [
42396         "Montenegro (Crna Gora)",
42397         "me",
42398         "382"
42399       ],
42400       [
42401         "Montserrat",
42402         "ms",
42403         "1664"
42404       ],
42405       [
42406         "Morocco (‫المغرب‬‎)",
42407         "ma",
42408         "212",
42409         0
42410       ],
42411       [
42412         "Mozambique (Moçambique)",
42413         "mz",
42414         "258"
42415       ],
42416       [
42417         "Myanmar (Burma) (မြန်မာ)",
42418         "mm",
42419         "95"
42420       ],
42421       [
42422         "Namibia (Namibië)",
42423         "na",
42424         "264"
42425       ],
42426       [
42427         "Nauru",
42428         "nr",
42429         "674"
42430       ],
42431       [
42432         "Nepal (नेपाल)",
42433         "np",
42434         "977"
42435       ],
42436       [
42437         "Netherlands (Nederland)",
42438         "nl",
42439         "31"
42440       ],
42441       [
42442         "New Caledonia (Nouvelle-Calédonie)",
42443         "nc",
42444         "687"
42445       ],
42446       [
42447         "New Zealand",
42448         "nz",
42449         "64"
42450       ],
42451       [
42452         "Nicaragua",
42453         "ni",
42454         "505"
42455       ],
42456       [
42457         "Niger (Nijar)",
42458         "ne",
42459         "227"
42460       ],
42461       [
42462         "Nigeria",
42463         "ng",
42464         "234"
42465       ],
42466       [
42467         "Niue",
42468         "nu",
42469         "683"
42470       ],
42471       [
42472         "Norfolk Island",
42473         "nf",
42474         "672"
42475       ],
42476       [
42477         "North Korea (조선 민주주의 인민 공화국)",
42478         "kp",
42479         "850"
42480       ],
42481       [
42482         "Northern Mariana Islands",
42483         "mp",
42484         "1670"
42485       ],
42486       [
42487         "Norway (Norge)",
42488         "no",
42489         "47",
42490         0
42491       ],
42492       [
42493         "Oman (‫عُمان‬‎)",
42494         "om",
42495         "968"
42496       ],
42497       [
42498         "Pakistan (‫پاکستان‬‎)",
42499         "pk",
42500         "92"
42501       ],
42502       [
42503         "Palau",
42504         "pw",
42505         "680"
42506       ],
42507       [
42508         "Palestine (‫فلسطين‬‎)",
42509         "ps",
42510         "970"
42511       ],
42512       [
42513         "Panama (Panamá)",
42514         "pa",
42515         "507"
42516       ],
42517       [
42518         "Papua New Guinea",
42519         "pg",
42520         "675"
42521       ],
42522       [
42523         "Paraguay",
42524         "py",
42525         "595"
42526       ],
42527       [
42528         "Peru (Perú)",
42529         "pe",
42530         "51"
42531       ],
42532       [
42533         "Philippines",
42534         "ph",
42535         "63"
42536       ],
42537       [
42538         "Poland (Polska)",
42539         "pl",
42540         "48"
42541       ],
42542       [
42543         "Portugal",
42544         "pt",
42545         "351"
42546       ],
42547       [
42548         "Puerto Rico",
42549         "pr",
42550         "1",
42551         3,
42552         ["787", "939"]
42553       ],
42554       [
42555         "Qatar (‫قطر‬‎)",
42556         "qa",
42557         "974"
42558       ],
42559       [
42560         "Réunion (La Réunion)",
42561         "re",
42562         "262",
42563         0
42564       ],
42565       [
42566         "Romania (România)",
42567         "ro",
42568         "40"
42569       ],
42570       [
42571         "Russia (Россия)",
42572         "ru",
42573         "7",
42574         0
42575       ],
42576       [
42577         "Rwanda",
42578         "rw",
42579         "250"
42580       ],
42581       [
42582         "Saint Barthélemy",
42583         "bl",
42584         "590",
42585         1
42586       ],
42587       [
42588         "Saint Helena",
42589         "sh",
42590         "290"
42591       ],
42592       [
42593         "Saint Kitts and Nevis",
42594         "kn",
42595         "1869"
42596       ],
42597       [
42598         "Saint Lucia",
42599         "lc",
42600         "1758"
42601       ],
42602       [
42603         "Saint Martin (Saint-Martin (partie française))",
42604         "mf",
42605         "590",
42606         2
42607       ],
42608       [
42609         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42610         "pm",
42611         "508"
42612       ],
42613       [
42614         "Saint Vincent and the Grenadines",
42615         "vc",
42616         "1784"
42617       ],
42618       [
42619         "Samoa",
42620         "ws",
42621         "685"
42622       ],
42623       [
42624         "San Marino",
42625         "sm",
42626         "378"
42627       ],
42628       [
42629         "São Tomé and Príncipe (São Tomé e Príncipe)",
42630         "st",
42631         "239"
42632       ],
42633       [
42634         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42635         "sa",
42636         "966"
42637       ],
42638       [
42639         "Senegal (Sénégal)",
42640         "sn",
42641         "221"
42642       ],
42643       [
42644         "Serbia (Србија)",
42645         "rs",
42646         "381"
42647       ],
42648       [
42649         "Seychelles",
42650         "sc",
42651         "248"
42652       ],
42653       [
42654         "Sierra Leone",
42655         "sl",
42656         "232"
42657       ],
42658       [
42659         "Singapore",
42660         "sg",
42661         "65"
42662       ],
42663       [
42664         "Sint Maarten",
42665         "sx",
42666         "1721"
42667       ],
42668       [
42669         "Slovakia (Slovensko)",
42670         "sk",
42671         "421"
42672       ],
42673       [
42674         "Slovenia (Slovenija)",
42675         "si",
42676         "386"
42677       ],
42678       [
42679         "Solomon Islands",
42680         "sb",
42681         "677"
42682       ],
42683       [
42684         "Somalia (Soomaaliya)",
42685         "so",
42686         "252"
42687       ],
42688       [
42689         "South Africa",
42690         "za",
42691         "27"
42692       ],
42693       [
42694         "South Korea (대한민국)",
42695         "kr",
42696         "82"
42697       ],
42698       [
42699         "South Sudan (‫جنوب السودان‬‎)",
42700         "ss",
42701         "211"
42702       ],
42703       [
42704         "Spain (España)",
42705         "es",
42706         "34"
42707       ],
42708       [
42709         "Sri Lanka (ශ්‍රී ලංකාව)",
42710         "lk",
42711         "94"
42712       ],
42713       [
42714         "Sudan (‫السودان‬‎)",
42715         "sd",
42716         "249"
42717       ],
42718       [
42719         "Suriname",
42720         "sr",
42721         "597"
42722       ],
42723       [
42724         "Svalbard and Jan Mayen",
42725         "sj",
42726         "47",
42727         1
42728       ],
42729       [
42730         "Swaziland",
42731         "sz",
42732         "268"
42733       ],
42734       [
42735         "Sweden (Sverige)",
42736         "se",
42737         "46"
42738       ],
42739       [
42740         "Switzerland (Schweiz)",
42741         "ch",
42742         "41"
42743       ],
42744       [
42745         "Syria (‫سوريا‬‎)",
42746         "sy",
42747         "963"
42748       ],
42749       [
42750         "Taiwan (台灣)",
42751         "tw",
42752         "886"
42753       ],
42754       [
42755         "Tajikistan",
42756         "tj",
42757         "992"
42758       ],
42759       [
42760         "Tanzania",
42761         "tz",
42762         "255"
42763       ],
42764       [
42765         "Thailand (ไทย)",
42766         "th",
42767         "66"
42768       ],
42769       [
42770         "Timor-Leste",
42771         "tl",
42772         "670"
42773       ],
42774       [
42775         "Togo",
42776         "tg",
42777         "228"
42778       ],
42779       [
42780         "Tokelau",
42781         "tk",
42782         "690"
42783       ],
42784       [
42785         "Tonga",
42786         "to",
42787         "676"
42788       ],
42789       [
42790         "Trinidad and Tobago",
42791         "tt",
42792         "1868"
42793       ],
42794       [
42795         "Tunisia (‫تونس‬‎)",
42796         "tn",
42797         "216"
42798       ],
42799       [
42800         "Turkey (Türkiye)",
42801         "tr",
42802         "90"
42803       ],
42804       [
42805         "Turkmenistan",
42806         "tm",
42807         "993"
42808       ],
42809       [
42810         "Turks and Caicos Islands",
42811         "tc",
42812         "1649"
42813       ],
42814       [
42815         "Tuvalu",
42816         "tv",
42817         "688"
42818       ],
42819       [
42820         "U.S. Virgin Islands",
42821         "vi",
42822         "1340"
42823       ],
42824       [
42825         "Uganda",
42826         "ug",
42827         "256"
42828       ],
42829       [
42830         "Ukraine (Україна)",
42831         "ua",
42832         "380"
42833       ],
42834       [
42835         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42836         "ae",
42837         "971"
42838       ],
42839       [
42840         "United Kingdom",
42841         "gb",
42842         "44",
42843         0
42844       ],
42845       [
42846         "United States",
42847         "us",
42848         "1",
42849         0
42850       ],
42851       [
42852         "Uruguay",
42853         "uy",
42854         "598"
42855       ],
42856       [
42857         "Uzbekistan (Oʻzbekiston)",
42858         "uz",
42859         "998"
42860       ],
42861       [
42862         "Vanuatu",
42863         "vu",
42864         "678"
42865       ],
42866       [
42867         "Vatican City (Città del Vaticano)",
42868         "va",
42869         "39",
42870         1
42871       ],
42872       [
42873         "Venezuela",
42874         "ve",
42875         "58"
42876       ],
42877       [
42878         "Vietnam (Việt Nam)",
42879         "vn",
42880         "84"
42881       ],
42882       [
42883         "Wallis and Futuna (Wallis-et-Futuna)",
42884         "wf",
42885         "681"
42886       ],
42887       [
42888         "Western Sahara (‫الصحراء الغربية‬‎)",
42889         "eh",
42890         "212",
42891         1
42892       ],
42893       [
42894         "Yemen (‫اليمن‬‎)",
42895         "ye",
42896         "967"
42897       ],
42898       [
42899         "Zambia",
42900         "zm",
42901         "260"
42902       ],
42903       [
42904         "Zimbabwe",
42905         "zw",
42906         "263"
42907       ],
42908       [
42909         "Åland Islands",
42910         "ax",
42911         "358",
42912         1
42913       ]
42914   ];
42915   
42916   return d;
42917 }/**
42918 *    This script refer to:
42919 *    Title: International Telephone Input
42920 *    Author: Jack O'Connor
42921 *    Code version:  v12.1.12
42922 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42923 **/
42924
42925 /**
42926  * @class Roo.bootstrap.PhoneInput
42927  * @extends Roo.bootstrap.TriggerField
42928  * An input with International dial-code selection
42929  
42930  * @cfg {String} defaultDialCode default '+852'
42931  * @cfg {Array} preferedCountries default []
42932   
42933  * @constructor
42934  * Create a new PhoneInput.
42935  * @param {Object} config Configuration options
42936  */
42937
42938 Roo.bootstrap.PhoneInput = function(config) {
42939     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42940 };
42941
42942 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42943         
42944         listWidth: undefined,
42945         
42946         selectedClass: 'active',
42947         
42948         invalidClass : "has-warning",
42949         
42950         validClass: 'has-success',
42951         
42952         allowed: '0123456789',
42953         
42954         max_length: 15,
42955         
42956         /**
42957          * @cfg {String} defaultDialCode The default dial code when initializing the input
42958          */
42959         defaultDialCode: '+852',
42960         
42961         /**
42962          * @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
42963          */
42964         preferedCountries: false,
42965         
42966         getAutoCreate : function()
42967         {
42968             var data = Roo.bootstrap.PhoneInputData();
42969             var align = this.labelAlign || this.parentLabelAlign();
42970             var id = Roo.id();
42971             
42972             this.allCountries = [];
42973             this.dialCodeMapping = [];
42974             
42975             for (var i = 0; i < data.length; i++) {
42976               var c = data[i];
42977               this.allCountries[i] = {
42978                 name: c[0],
42979                 iso2: c[1],
42980                 dialCode: c[2],
42981                 priority: c[3] || 0,
42982                 areaCodes: c[4] || null
42983               };
42984               this.dialCodeMapping[c[2]] = {
42985                   name: c[0],
42986                   iso2: c[1],
42987                   priority: c[3] || 0,
42988                   areaCodes: c[4] || null
42989               };
42990             }
42991             
42992             var cfg = {
42993                 cls: 'form-group',
42994                 cn: []
42995             };
42996             
42997             var input =  {
42998                 tag: 'input',
42999                 id : id,
43000                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43001                 maxlength: this.max_length,
43002                 cls : 'form-control tel-input',
43003                 autocomplete: 'new-password'
43004             };
43005             
43006             var hiddenInput = {
43007                 tag: 'input',
43008                 type: 'hidden',
43009                 cls: 'hidden-tel-input'
43010             };
43011             
43012             if (this.name) {
43013                 hiddenInput.name = this.name;
43014             }
43015             
43016             if (this.disabled) {
43017                 input.disabled = true;
43018             }
43019             
43020             var flag_container = {
43021                 tag: 'div',
43022                 cls: 'flag-box',
43023                 cn: [
43024                     {
43025                         tag: 'div',
43026                         cls: 'flag'
43027                     },
43028                     {
43029                         tag: 'div',
43030                         cls: 'caret'
43031                     }
43032                 ]
43033             };
43034             
43035             var box = {
43036                 tag: 'div',
43037                 cls: this.hasFeedback ? 'has-feedback' : '',
43038                 cn: [
43039                     hiddenInput,
43040                     input,
43041                     {
43042                         tag: 'input',
43043                         cls: 'dial-code-holder',
43044                         disabled: true
43045                     }
43046                 ]
43047             };
43048             
43049             var container = {
43050                 cls: 'roo-select2-container input-group',
43051                 cn: [
43052                     flag_container,
43053                     box
43054                 ]
43055             };
43056             
43057             if (this.fieldLabel.length) {
43058                 var indicator = {
43059                     tag: 'i',
43060                     tooltip: 'This field is required'
43061                 };
43062                 
43063                 var label = {
43064                     tag: 'label',
43065                     'for':  id,
43066                     cls: 'control-label',
43067                     cn: []
43068                 };
43069                 
43070                 var label_text = {
43071                     tag: 'span',
43072                     html: this.fieldLabel
43073                 };
43074                 
43075                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43076                 label.cn = [
43077                     indicator,
43078                     label_text
43079                 ];
43080                 
43081                 if(this.indicatorpos == 'right') {
43082                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43083                     label.cn = [
43084                         label_text,
43085                         indicator
43086                     ];
43087                 }
43088                 
43089                 if(align == 'left') {
43090                     container = {
43091                         tag: 'div',
43092                         cn: [
43093                             container
43094                         ]
43095                     };
43096                     
43097                     if(this.labelWidth > 12){
43098                         label.style = "width: " + this.labelWidth + 'px';
43099                     }
43100                     if(this.labelWidth < 13 && this.labelmd == 0){
43101                         this.labelmd = this.labelWidth;
43102                     }
43103                     if(this.labellg > 0){
43104                         label.cls += ' col-lg-' + this.labellg;
43105                         input.cls += ' col-lg-' + (12 - this.labellg);
43106                     }
43107                     if(this.labelmd > 0){
43108                         label.cls += ' col-md-' + this.labelmd;
43109                         container.cls += ' col-md-' + (12 - this.labelmd);
43110                     }
43111                     if(this.labelsm > 0){
43112                         label.cls += ' col-sm-' + this.labelsm;
43113                         container.cls += ' col-sm-' + (12 - this.labelsm);
43114                     }
43115                     if(this.labelxs > 0){
43116                         label.cls += ' col-xs-' + this.labelxs;
43117                         container.cls += ' col-xs-' + (12 - this.labelxs);
43118                     }
43119                 }
43120             }
43121             
43122             cfg.cn = [
43123                 label,
43124                 container
43125             ];
43126             
43127             var settings = this;
43128             
43129             ['xs','sm','md','lg'].map(function(size){
43130                 if (settings[size]) {
43131                     cfg.cls += ' col-' + size + '-' + settings[size];
43132                 }
43133             });
43134             
43135             this.store = new Roo.data.Store({
43136                 proxy : new Roo.data.MemoryProxy({}),
43137                 reader : new Roo.data.JsonReader({
43138                     fields : [
43139                         {
43140                             'name' : 'name',
43141                             'type' : 'string'
43142                         },
43143                         {
43144                             'name' : 'iso2',
43145                             'type' : 'string'
43146                         },
43147                         {
43148                             'name' : 'dialCode',
43149                             'type' : 'string'
43150                         },
43151                         {
43152                             'name' : 'priority',
43153                             'type' : 'string'
43154                         },
43155                         {
43156                             'name' : 'areaCodes',
43157                             'type' : 'string'
43158                         }
43159                     ]
43160                 })
43161             });
43162             
43163             if(!this.preferedCountries) {
43164                 this.preferedCountries = [
43165                     'hk',
43166                     'gb',
43167                     'us'
43168                 ];
43169             }
43170             
43171             var p = this.preferedCountries.reverse();
43172             
43173             if(p) {
43174                 for (var i = 0; i < p.length; i++) {
43175                     for (var j = 0; j < this.allCountries.length; j++) {
43176                         if(this.allCountries[j].iso2 == p[i]) {
43177                             var t = this.allCountries[j];
43178                             this.allCountries.splice(j,1);
43179                             this.allCountries.unshift(t);
43180                         }
43181                     } 
43182                 }
43183             }
43184             
43185             this.store.proxy.data = {
43186                 success: true,
43187                 data: this.allCountries
43188             };
43189             
43190             return cfg;
43191         },
43192         
43193         initEvents : function()
43194         {
43195             this.createList();
43196             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43197             
43198             this.indicator = this.indicatorEl();
43199             this.flag = this.flagEl();
43200             this.dialCodeHolder = this.dialCodeHolderEl();
43201             
43202             this.trigger = this.el.select('div.flag-box',true).first();
43203             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43204             
43205             var _this = this;
43206             
43207             (function(){
43208                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43209                 _this.list.setWidth(lw);
43210             }).defer(100);
43211             
43212             this.list.on('mouseover', this.onViewOver, this);
43213             this.list.on('mousemove', this.onViewMove, this);
43214             this.inputEl().on("keyup", this.onKeyUp, this);
43215             this.inputEl().on("keypress", this.onKeyPress, this);
43216             
43217             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43218
43219             this.view = new Roo.View(this.list, this.tpl, {
43220                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43221             });
43222             
43223             this.view.on('click', this.onViewClick, this);
43224             this.setValue(this.defaultDialCode);
43225         },
43226         
43227         onTriggerClick : function(e)
43228         {
43229             Roo.log('trigger click');
43230             if(this.disabled){
43231                 return;
43232             }
43233             
43234             if(this.isExpanded()){
43235                 this.collapse();
43236                 this.hasFocus = false;
43237             }else {
43238                 this.store.load({});
43239                 this.hasFocus = true;
43240                 this.expand();
43241             }
43242         },
43243         
43244         isExpanded : function()
43245         {
43246             return this.list.isVisible();
43247         },
43248         
43249         collapse : function()
43250         {
43251             if(!this.isExpanded()){
43252                 return;
43253             }
43254             this.list.hide();
43255             Roo.get(document).un('mousedown', this.collapseIf, this);
43256             Roo.get(document).un('mousewheel', this.collapseIf, this);
43257             this.fireEvent('collapse', this);
43258             this.validate();
43259         },
43260         
43261         expand : function()
43262         {
43263             Roo.log('expand');
43264
43265             if(this.isExpanded() || !this.hasFocus){
43266                 return;
43267             }
43268             
43269             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43270             this.list.setWidth(lw);
43271             
43272             this.list.show();
43273             this.restrictHeight();
43274             
43275             Roo.get(document).on('mousedown', this.collapseIf, this);
43276             Roo.get(document).on('mousewheel', this.collapseIf, this);
43277             
43278             this.fireEvent('expand', this);
43279         },
43280         
43281         restrictHeight : function()
43282         {
43283             this.list.alignTo(this.inputEl(), this.listAlign);
43284             this.list.alignTo(this.inputEl(), this.listAlign);
43285         },
43286         
43287         onViewOver : function(e, t)
43288         {
43289             if(this.inKeyMode){
43290                 return;
43291             }
43292             var item = this.view.findItemFromChild(t);
43293             
43294             if(item){
43295                 var index = this.view.indexOf(item);
43296                 this.select(index, false);
43297             }
43298         },
43299
43300         // private
43301         onViewClick : function(view, doFocus, el, e)
43302         {
43303             var index = this.view.getSelectedIndexes()[0];
43304             
43305             var r = this.store.getAt(index);
43306             
43307             if(r){
43308                 this.onSelect(r, index);
43309             }
43310             if(doFocus !== false && !this.blockFocus){
43311                 this.inputEl().focus();
43312             }
43313         },
43314         
43315         onViewMove : function(e, t)
43316         {
43317             this.inKeyMode = false;
43318         },
43319         
43320         select : function(index, scrollIntoView)
43321         {
43322             this.selectedIndex = index;
43323             this.view.select(index);
43324             if(scrollIntoView !== false){
43325                 var el = this.view.getNode(index);
43326                 if(el){
43327                     this.list.scrollChildIntoView(el, false);
43328                 }
43329             }
43330         },
43331         
43332         createList : function()
43333         {
43334             this.list = Roo.get(document.body).createChild({
43335                 tag: 'ul',
43336                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43337                 style: 'display:none'
43338             });
43339             
43340             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43341         },
43342         
43343         collapseIf : function(e)
43344         {
43345             var in_combo  = e.within(this.el);
43346             var in_list =  e.within(this.list);
43347             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43348             
43349             if (in_combo || in_list || is_list) {
43350                 return;
43351             }
43352             this.collapse();
43353         },
43354         
43355         onSelect : function(record, index)
43356         {
43357             if(this.fireEvent('beforeselect', this, record, index) !== false){
43358                 
43359                 this.setFlagClass(record.data.iso2);
43360                 this.setDialCode(record.data.dialCode);
43361                 this.hasFocus = false;
43362                 this.collapse();
43363                 this.fireEvent('select', this, record, index);
43364             }
43365         },
43366         
43367         flagEl : function()
43368         {
43369             var flag = this.el.select('div.flag',true).first();
43370             if(!flag){
43371                 return false;
43372             }
43373             return flag;
43374         },
43375         
43376         dialCodeHolderEl : function()
43377         {
43378             var d = this.el.select('input.dial-code-holder',true).first();
43379             if(!d){
43380                 return false;
43381             }
43382             return d;
43383         },
43384         
43385         setDialCode : function(v)
43386         {
43387             this.dialCodeHolder.dom.value = '+'+v;
43388         },
43389         
43390         setFlagClass : function(n)
43391         {
43392             this.flag.dom.className = 'flag '+n;
43393         },
43394         
43395         getValue : function()
43396         {
43397             var v = this.inputEl().getValue();
43398             if(this.dialCodeHolder) {
43399                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43400             }
43401             return v;
43402         },
43403         
43404         setValue : function(v)
43405         {
43406             var d = this.getDialCode(v);
43407             
43408             //invalid dial code
43409             if(v.length == 0 || !d || d.length == 0) {
43410                 if(this.rendered){
43411                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43412                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43413                 }
43414                 return;
43415             }
43416             
43417             //valid dial code
43418             this.setFlagClass(this.dialCodeMapping[d].iso2);
43419             this.setDialCode(d);
43420             this.inputEl().dom.value = v.replace('+'+d,'');
43421             this.hiddenEl().dom.value = this.getValue();
43422             
43423             this.validate();
43424         },
43425         
43426         getDialCode : function(v)
43427         {
43428             v = v ||  '';
43429             
43430             if (v.length == 0) {
43431                 return this.dialCodeHolder.dom.value;
43432             }
43433             
43434             var dialCode = "";
43435             if (v.charAt(0) != "+") {
43436                 return false;
43437             }
43438             var numericChars = "";
43439             for (var i = 1; i < v.length; i++) {
43440               var c = v.charAt(i);
43441               if (!isNaN(c)) {
43442                 numericChars += c;
43443                 if (this.dialCodeMapping[numericChars]) {
43444                   dialCode = v.substr(1, i);
43445                 }
43446                 if (numericChars.length == 4) {
43447                   break;
43448                 }
43449               }
43450             }
43451             return dialCode;
43452         },
43453         
43454         reset : function()
43455         {
43456             this.setValue(this.defaultDialCode);
43457             this.validate();
43458         },
43459         
43460         hiddenEl : function()
43461         {
43462             return this.el.select('input.hidden-tel-input',true).first();
43463         },
43464         
43465         // after setting val
43466         onKeyUp : function(e){
43467             this.setValue(this.getValue());
43468         },
43469         
43470         onKeyPress : function(e){
43471             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43472                 e.stopEvent();
43473             }
43474         }
43475         
43476 });
43477 /**
43478  * @class Roo.bootstrap.MoneyField
43479  * @extends Roo.bootstrap.ComboBox
43480  * Bootstrap MoneyField class
43481  * 
43482  * @constructor
43483  * Create a new MoneyField.
43484  * @param {Object} config Configuration options
43485  */
43486
43487 Roo.bootstrap.MoneyField = function(config) {
43488     
43489     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43490     
43491 };
43492
43493 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43494     
43495     /**
43496      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43497      */
43498     allowDecimals : true,
43499     /**
43500      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43501      */
43502     decimalSeparator : ".",
43503     /**
43504      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43505      */
43506     decimalPrecision : 0,
43507     /**
43508      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43509      */
43510     allowNegative : true,
43511     /**
43512      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43513      */
43514     allowZero: true,
43515     /**
43516      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43517      */
43518     minValue : Number.NEGATIVE_INFINITY,
43519     /**
43520      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43521      */
43522     maxValue : Number.MAX_VALUE,
43523     /**
43524      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43525      */
43526     minText : "The minimum value for this field is {0}",
43527     /**
43528      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43529      */
43530     maxText : "The maximum value for this field is {0}",
43531     /**
43532      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43533      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43534      */
43535     nanText : "{0} is not a valid number",
43536     /**
43537      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43538      */
43539     castInt : true,
43540     /**
43541      * @cfg {String} defaults currency of the MoneyField
43542      * value should be in lkey
43543      */
43544     defaultCurrency : false,
43545     /**
43546      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43547      */
43548     thousandsDelimiter : false,
43549     /**
43550      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43551      */
43552     max_length: false,
43553     
43554     inputlg : 9,
43555     inputmd : 9,
43556     inputsm : 9,
43557     inputxs : 6,
43558     
43559     store : false,
43560     
43561     getAutoCreate : function()
43562     {
43563         var align = this.labelAlign || this.parentLabelAlign();
43564         
43565         var id = Roo.id();
43566
43567         var cfg = {
43568             cls: 'form-group',
43569             cn: []
43570         };
43571
43572         var input =  {
43573             tag: 'input',
43574             id : id,
43575             cls : 'form-control roo-money-amount-input',
43576             autocomplete: 'new-password'
43577         };
43578         
43579         var hiddenInput = {
43580             tag: 'input',
43581             type: 'hidden',
43582             id: Roo.id(),
43583             cls: 'hidden-number-input'
43584         };
43585         
43586         if(this.max_length) {
43587             input.maxlength = this.max_length; 
43588         }
43589         
43590         if (this.name) {
43591             hiddenInput.name = this.name;
43592         }
43593
43594         if (this.disabled) {
43595             input.disabled = true;
43596         }
43597
43598         var clg = 12 - this.inputlg;
43599         var cmd = 12 - this.inputmd;
43600         var csm = 12 - this.inputsm;
43601         var cxs = 12 - this.inputxs;
43602         
43603         var container = {
43604             tag : 'div',
43605             cls : 'row roo-money-field',
43606             cn : [
43607                 {
43608                     tag : 'div',
43609                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43610                     cn : [
43611                         {
43612                             tag : 'div',
43613                             cls: 'roo-select2-container input-group',
43614                             cn: [
43615                                 {
43616                                     tag : 'input',
43617                                     cls : 'form-control roo-money-currency-input',
43618                                     autocomplete: 'new-password',
43619                                     readOnly : 1,
43620                                     name : this.currencyName
43621                                 },
43622                                 {
43623                                     tag :'span',
43624                                     cls : 'input-group-addon',
43625                                     cn : [
43626                                         {
43627                                             tag: 'span',
43628                                             cls: 'caret'
43629                                         }
43630                                     ]
43631                                 }
43632                             ]
43633                         }
43634                     ]
43635                 },
43636                 {
43637                     tag : 'div',
43638                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43639                     cn : [
43640                         {
43641                             tag: 'div',
43642                             cls: this.hasFeedback ? 'has-feedback' : '',
43643                             cn: [
43644                                 input
43645                             ]
43646                         }
43647                     ]
43648                 }
43649             ]
43650             
43651         };
43652         
43653         if (this.fieldLabel.length) {
43654             var indicator = {
43655                 tag: 'i',
43656                 tooltip: 'This field is required'
43657             };
43658
43659             var label = {
43660                 tag: 'label',
43661                 'for':  id,
43662                 cls: 'control-label',
43663                 cn: []
43664             };
43665
43666             var label_text = {
43667                 tag: 'span',
43668                 html: this.fieldLabel
43669             };
43670
43671             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43672             label.cn = [
43673                 indicator,
43674                 label_text
43675             ];
43676
43677             if(this.indicatorpos == 'right') {
43678                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43679                 label.cn = [
43680                     label_text,
43681                     indicator
43682                 ];
43683             }
43684
43685             if(align == 'left') {
43686                 container = {
43687                     tag: 'div',
43688                     cn: [
43689                         container
43690                     ]
43691                 };
43692
43693                 if(this.labelWidth > 12){
43694                     label.style = "width: " + this.labelWidth + 'px';
43695                 }
43696                 if(this.labelWidth < 13 && this.labelmd == 0){
43697                     this.labelmd = this.labelWidth;
43698                 }
43699                 if(this.labellg > 0){
43700                     label.cls += ' col-lg-' + this.labellg;
43701                     input.cls += ' col-lg-' + (12 - this.labellg);
43702                 }
43703                 if(this.labelmd > 0){
43704                     label.cls += ' col-md-' + this.labelmd;
43705                     container.cls += ' col-md-' + (12 - this.labelmd);
43706                 }
43707                 if(this.labelsm > 0){
43708                     label.cls += ' col-sm-' + this.labelsm;
43709                     container.cls += ' col-sm-' + (12 - this.labelsm);
43710                 }
43711                 if(this.labelxs > 0){
43712                     label.cls += ' col-xs-' + this.labelxs;
43713                     container.cls += ' col-xs-' + (12 - this.labelxs);
43714                 }
43715             }
43716         }
43717
43718         cfg.cn = [
43719             label,
43720             container,
43721             hiddenInput
43722         ];
43723         
43724         var settings = this;
43725
43726         ['xs','sm','md','lg'].map(function(size){
43727             if (settings[size]) {
43728                 cfg.cls += ' col-' + size + '-' + settings[size];
43729             }
43730         });
43731         
43732         return cfg;
43733     },
43734     
43735     initEvents : function()
43736     {
43737         this.indicator = this.indicatorEl();
43738         
43739         this.initCurrencyEvent();
43740         
43741         this.initNumberEvent();
43742     },
43743     
43744     initCurrencyEvent : function()
43745     {
43746         if (!this.store) {
43747             throw "can not find store for combo";
43748         }
43749         
43750         this.store = Roo.factory(this.store, Roo.data);
43751         this.store.parent = this;
43752         
43753         this.createList();
43754         
43755         this.triggerEl = this.el.select('.input-group-addon', true).first();
43756         
43757         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43758         
43759         var _this = this;
43760         
43761         (function(){
43762             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43763             _this.list.setWidth(lw);
43764         }).defer(100);
43765         
43766         this.list.on('mouseover', this.onViewOver, this);
43767         this.list.on('mousemove', this.onViewMove, this);
43768         this.list.on('scroll', this.onViewScroll, this);
43769         
43770         if(!this.tpl){
43771             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43772         }
43773         
43774         this.view = new Roo.View(this.list, this.tpl, {
43775             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43776         });
43777         
43778         this.view.on('click', this.onViewClick, this);
43779         
43780         this.store.on('beforeload', this.onBeforeLoad, this);
43781         this.store.on('load', this.onLoad, this);
43782         this.store.on('loadexception', this.onLoadException, this);
43783         
43784         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43785             "up" : function(e){
43786                 this.inKeyMode = true;
43787                 this.selectPrev();
43788             },
43789
43790             "down" : function(e){
43791                 if(!this.isExpanded()){
43792                     this.onTriggerClick();
43793                 }else{
43794                     this.inKeyMode = true;
43795                     this.selectNext();
43796                 }
43797             },
43798
43799             "enter" : function(e){
43800                 this.collapse();
43801                 
43802                 if(this.fireEvent("specialkey", this, e)){
43803                     this.onViewClick(false);
43804                 }
43805                 
43806                 return true;
43807             },
43808
43809             "esc" : function(e){
43810                 this.collapse();
43811             },
43812
43813             "tab" : function(e){
43814                 this.collapse();
43815                 
43816                 if(this.fireEvent("specialkey", this, e)){
43817                     this.onViewClick(false);
43818                 }
43819                 
43820                 return true;
43821             },
43822
43823             scope : this,
43824
43825             doRelay : function(foo, bar, hname){
43826                 if(hname == 'down' || this.scope.isExpanded()){
43827                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43828                 }
43829                 return true;
43830             },
43831
43832             forceKeyDown: true
43833         });
43834         
43835         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43836         
43837     },
43838     
43839     initNumberEvent : function(e)
43840     {
43841         this.inputEl().on("keydown" , this.fireKey,  this);
43842         this.inputEl().on("focus", this.onFocus,  this);
43843         this.inputEl().on("blur", this.onBlur,  this);
43844         
43845         this.inputEl().relayEvent('keyup', this);
43846         
43847         if(this.indicator){
43848             this.indicator.addClass('invisible');
43849         }
43850  
43851         this.originalValue = this.getValue();
43852         
43853         if(this.validationEvent == 'keyup'){
43854             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43855             this.inputEl().on('keyup', this.filterValidation, this);
43856         }
43857         else if(this.validationEvent !== false){
43858             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43859         }
43860         
43861         if(this.selectOnFocus){
43862             this.on("focus", this.preFocus, this);
43863             
43864         }
43865         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43866             this.inputEl().on("keypress", this.filterKeys, this);
43867         } else {
43868             this.inputEl().relayEvent('keypress', this);
43869         }
43870         
43871         var allowed = "0123456789";
43872         
43873         if(this.allowDecimals){
43874             allowed += this.decimalSeparator;
43875         }
43876         
43877         if(this.allowNegative){
43878             allowed += "-";
43879         }
43880         
43881         if(this.thousandsDelimiter) {
43882             allowed += ",";
43883         }
43884         
43885         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43886         
43887         var keyPress = function(e){
43888             
43889             var k = e.getKey();
43890             
43891             var c = e.getCharCode();
43892             
43893             if(
43894                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43895                     allowed.indexOf(String.fromCharCode(c)) === -1
43896             ){
43897                 e.stopEvent();
43898                 return;
43899             }
43900             
43901             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43902                 return;
43903             }
43904             
43905             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43906                 e.stopEvent();
43907             }
43908         };
43909         
43910         this.inputEl().on("keypress", keyPress, this);
43911         
43912     },
43913     
43914     onTriggerClick : function(e)
43915     {   
43916         if(this.disabled){
43917             return;
43918         }
43919         
43920         this.page = 0;
43921         this.loadNext = false;
43922         
43923         if(this.isExpanded()){
43924             this.collapse();
43925             return;
43926         }
43927         
43928         this.hasFocus = true;
43929         
43930         if(this.triggerAction == 'all') {
43931             this.doQuery(this.allQuery, true);
43932             return;
43933         }
43934         
43935         this.doQuery(this.getRawValue());
43936     },
43937     
43938     getCurrency : function()
43939     {   
43940         var v = this.currencyEl().getValue();
43941         
43942         return v;
43943     },
43944     
43945     restrictHeight : function()
43946     {
43947         this.list.alignTo(this.currencyEl(), this.listAlign);
43948         this.list.alignTo(this.currencyEl(), this.listAlign);
43949     },
43950     
43951     onViewClick : function(view, doFocus, el, e)
43952     {
43953         var index = this.view.getSelectedIndexes()[0];
43954         
43955         var r = this.store.getAt(index);
43956         
43957         if(r){
43958             this.onSelect(r, index);
43959         }
43960     },
43961     
43962     onSelect : function(record, index){
43963         
43964         if(this.fireEvent('beforeselect', this, record, index) !== false){
43965         
43966             this.setFromCurrencyData(index > -1 ? record.data : false);
43967             
43968             this.collapse();
43969             
43970             this.fireEvent('select', this, record, index);
43971         }
43972     },
43973     
43974     setFromCurrencyData : function(o)
43975     {
43976         var currency = '';
43977         
43978         this.lastCurrency = o;
43979         
43980         if (this.currencyField) {
43981             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43982         } else {
43983             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43984         }
43985         
43986         this.lastSelectionText = currency;
43987         
43988         //setting default currency
43989         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43990             this.setCurrency(this.defaultCurrency);
43991             return;
43992         }
43993         
43994         this.setCurrency(currency);
43995     },
43996     
43997     setFromData : function(o)
43998     {
43999         var c = {};
44000         
44001         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44002         
44003         this.setFromCurrencyData(c);
44004         
44005         var value = '';
44006         
44007         if (this.name) {
44008             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44009         } else {
44010             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44011         }
44012         
44013         this.setValue(value);
44014         
44015     },
44016     
44017     setCurrency : function(v)
44018     {   
44019         this.currencyValue = v;
44020         
44021         if(this.rendered){
44022             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44023             this.validate();
44024         }
44025     },
44026     
44027     setValue : function(v)
44028     {
44029         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44030         
44031         this.value = v;
44032         
44033         if(this.rendered){
44034             
44035             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44036             
44037             this.inputEl().dom.value = (v == '') ? '' :
44038                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44039             
44040             if(!this.allowZero && v === '0') {
44041                 this.hiddenEl().dom.value = '';
44042                 this.inputEl().dom.value = '';
44043             }
44044             
44045             this.validate();
44046         }
44047     },
44048     
44049     getRawValue : function()
44050     {
44051         var v = this.inputEl().getValue();
44052         
44053         return v;
44054     },
44055     
44056     getValue : function()
44057     {
44058         return this.fixPrecision(this.parseValue(this.getRawValue()));
44059     },
44060     
44061     parseValue : function(value)
44062     {
44063         if(this.thousandsDelimiter) {
44064             value += "";
44065             r = new RegExp(",", "g");
44066             value = value.replace(r, "");
44067         }
44068         
44069         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44070         return isNaN(value) ? '' : value;
44071         
44072     },
44073     
44074     fixPrecision : function(value)
44075     {
44076         if(this.thousandsDelimiter) {
44077             value += "";
44078             r = new RegExp(",", "g");
44079             value = value.replace(r, "");
44080         }
44081         
44082         var nan = isNaN(value);
44083         
44084         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44085             return nan ? '' : value;
44086         }
44087         return parseFloat(value).toFixed(this.decimalPrecision);
44088     },
44089     
44090     decimalPrecisionFcn : function(v)
44091     {
44092         return Math.floor(v);
44093     },
44094     
44095     validateValue : function(value)
44096     {
44097         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44098             return false;
44099         }
44100         
44101         var num = this.parseValue(value);
44102         
44103         if(isNaN(num)){
44104             this.markInvalid(String.format(this.nanText, value));
44105             return false;
44106         }
44107         
44108         if(num < this.minValue){
44109             this.markInvalid(String.format(this.minText, this.minValue));
44110             return false;
44111         }
44112         
44113         if(num > this.maxValue){
44114             this.markInvalid(String.format(this.maxText, this.maxValue));
44115             return false;
44116         }
44117         
44118         return true;
44119     },
44120     
44121     validate : function()
44122     {
44123         if(this.disabled || this.allowBlank){
44124             this.markValid();
44125             return true;
44126         }
44127         
44128         var currency = this.getCurrency();
44129         
44130         if(this.validateValue(this.getRawValue()) && currency.length){
44131             this.markValid();
44132             return true;
44133         }
44134         
44135         this.markInvalid();
44136         return false;
44137     },
44138     
44139     getName: function()
44140     {
44141         return this.name;
44142     },
44143     
44144     beforeBlur : function()
44145     {
44146         if(!this.castInt){
44147             return;
44148         }
44149         
44150         var v = this.parseValue(this.getRawValue());
44151         
44152         if(v || v == 0){
44153             this.setValue(v);
44154         }
44155     },
44156     
44157     onBlur : function()
44158     {
44159         this.beforeBlur();
44160         
44161         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44162             //this.el.removeClass(this.focusClass);
44163         }
44164         
44165         this.hasFocus = false;
44166         
44167         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44168             this.validate();
44169         }
44170         
44171         var v = this.getValue();
44172         
44173         if(String(v) !== String(this.startValue)){
44174             this.fireEvent('change', this, v, this.startValue);
44175         }
44176         
44177         this.fireEvent("blur", this);
44178     },
44179     
44180     inputEl : function()
44181     {
44182         return this.el.select('.roo-money-amount-input', true).first();
44183     },
44184     
44185     currencyEl : function()
44186     {
44187         return this.el.select('.roo-money-currency-input', true).first();
44188     },
44189     
44190     hiddenEl : function()
44191     {
44192         return this.el.select('input.hidden-number-input',true).first();
44193     }
44194     
44195 });/**
44196  * @class Roo.bootstrap.BezierSignature
44197  * @extends Roo.bootstrap.Component
44198  * Bootstrap BezierSignature class
44199  * This script refer to:
44200  *    Title: Signature Pad
44201  *    Author: szimek
44202  *    Availability: https://github.com/szimek/signature_pad
44203  *
44204  * @constructor
44205  * Create a new BezierSignature
44206  * @param {Object} config The config object
44207  */
44208
44209 Roo.bootstrap.BezierSignature = function(config){
44210     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44211     this.addEvents({
44212         "resize" : true
44213     });
44214 };
44215
44216 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44217 {
44218      
44219     curve_data: [],
44220     
44221     is_empty: true,
44222     
44223     mouse_btn_down: true,
44224     
44225     /**
44226      * @cfg {int} canvas height
44227      */
44228     canvas_height: '200px',
44229     
44230     /**
44231      * @cfg {float|function} Radius of a single dot.
44232      */ 
44233     dot_size: false,
44234     
44235     /**
44236      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44237      */
44238     min_width: 0.5,
44239     
44240     /**
44241      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44242      */
44243     max_width: 2.5,
44244     
44245     /**
44246      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44247      */
44248     throttle: 16,
44249     
44250     /**
44251      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44252      */
44253     min_distance: 5,
44254     
44255     /**
44256      * @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.
44257      */
44258     bg_color: 'rgba(0, 0, 0, 0)',
44259     
44260     /**
44261      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44262      */
44263     dot_color: 'black',
44264     
44265     /**
44266      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44267      */ 
44268     velocity_filter_weight: 0.7,
44269     
44270     /**
44271      * @cfg {function} Callback when stroke begin. 
44272      */
44273     onBegin: false,
44274     
44275     /**
44276      * @cfg {function} Callback when stroke end.
44277      */
44278     onEnd: false,
44279     
44280     getAutoCreate : function()
44281     {
44282         var cls = 'roo-signature column';
44283         
44284         if(this.cls){
44285             cls += ' ' + this.cls;
44286         }
44287         
44288         var col_sizes = [
44289             'lg',
44290             'md',
44291             'sm',
44292             'xs'
44293         ];
44294         
44295         for(var i = 0; i < col_sizes.length; i++) {
44296             if(this[col_sizes[i]]) {
44297                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44298             }
44299         }
44300         
44301         var cfg = {
44302             tag: 'div',
44303             cls: cls,
44304             cn: [
44305                 {
44306                     tag: 'div',
44307                     cls: 'roo-signature-body',
44308                     cn: [
44309                         {
44310                             tag: 'canvas',
44311                             cls: 'roo-signature-body-canvas',
44312                             height: this.canvas_height,
44313                             width: this.canvas_width
44314                         }
44315                     ]
44316                 },
44317                 {
44318                     tag: 'input',
44319                     type: 'file',
44320                     style: 'display: none'
44321                 }
44322             ]
44323         };
44324         
44325         return cfg;
44326     },
44327     
44328     initEvents: function() 
44329     {
44330         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44331         
44332         var canvas = this.canvasEl();
44333         
44334         // mouse && touch event swapping...
44335         canvas.dom.style.touchAction = 'none';
44336         canvas.dom.style.msTouchAction = 'none';
44337         
44338         this.mouse_btn_down = false;
44339         canvas.on('mousedown', this._handleMouseDown, this);
44340         canvas.on('mousemove', this._handleMouseMove, this);
44341         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44342         
44343         if (window.PointerEvent) {
44344             canvas.on('pointerdown', this._handleMouseDown, this);
44345             canvas.on('pointermove', this._handleMouseMove, this);
44346             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44347         }
44348         
44349         if ('ontouchstart' in window) {
44350             canvas.on('touchstart', this._handleTouchStart, this);
44351             canvas.on('touchmove', this._handleTouchMove, this);
44352             canvas.on('touchend', this._handleTouchEnd, this);
44353         }
44354         
44355         Roo.EventManager.onWindowResize(this.resize, this, true);
44356         
44357         // file input event
44358         this.fileEl().on('change', this.uploadImage, this);
44359         
44360         this.clear();
44361         
44362         this.resize();
44363     },
44364     
44365     resize: function(){
44366         
44367         var canvas = this.canvasEl().dom;
44368         var ctx = this.canvasElCtx();
44369         var img_data = false;
44370         
44371         if(canvas.width > 0) {
44372             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44373         }
44374         // setting canvas width will clean img data
44375         canvas.width = 0;
44376         
44377         var style = window.getComputedStyle ? 
44378             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44379             
44380         var padding_left = parseInt(style.paddingLeft) || 0;
44381         var padding_right = parseInt(style.paddingRight) || 0;
44382         
44383         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44384         
44385         if(img_data) {
44386             ctx.putImageData(img_data, 0, 0);
44387         }
44388     },
44389     
44390     _handleMouseDown: function(e)
44391     {
44392         if (e.browserEvent.which === 1) {
44393             this.mouse_btn_down = true;
44394             this.strokeBegin(e);
44395         }
44396     },
44397     
44398     _handleMouseMove: function (e)
44399     {
44400         if (this.mouse_btn_down) {
44401             this.strokeMoveUpdate(e);
44402         }
44403     },
44404     
44405     _handleMouseUp: function (e)
44406     {
44407         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44408             this.mouse_btn_down = false;
44409             this.strokeEnd(e);
44410         }
44411     },
44412     
44413     _handleTouchStart: function (e) {
44414         
44415         e.preventDefault();
44416         if (e.browserEvent.targetTouches.length === 1) {
44417             // var touch = e.browserEvent.changedTouches[0];
44418             // this.strokeBegin(touch);
44419             
44420              this.strokeBegin(e); // assume e catching the correct xy...
44421         }
44422     },
44423     
44424     _handleTouchMove: function (e) {
44425         e.preventDefault();
44426         // var touch = event.targetTouches[0];
44427         // _this._strokeMoveUpdate(touch);
44428         this.strokeMoveUpdate(e);
44429     },
44430     
44431     _handleTouchEnd: function (e) {
44432         var wasCanvasTouched = e.target === this.canvasEl().dom;
44433         if (wasCanvasTouched) {
44434             e.preventDefault();
44435             // var touch = event.changedTouches[0];
44436             // _this._strokeEnd(touch);
44437             this.strokeEnd(e);
44438         }
44439     },
44440     
44441     reset: function () {
44442         this._lastPoints = [];
44443         this._lastVelocity = 0;
44444         this._lastWidth = (this.min_width + this.max_width) / 2;
44445         this.canvasElCtx().fillStyle = this.dot_color;
44446     },
44447     
44448     strokeMoveUpdate: function(e)
44449     {
44450         this.strokeUpdate(e);
44451         
44452         if (this.throttle) {
44453             this.throttleStroke(this.strokeUpdate, this.throttle);
44454         }
44455         else {
44456             this.strokeUpdate(e);
44457         }
44458     },
44459     
44460     strokeBegin: function(e)
44461     {
44462         var newPointGroup = {
44463             color: this.dot_color,
44464             points: []
44465         };
44466         
44467         if (typeof this.onBegin === 'function') {
44468             this.onBegin(e);
44469         }
44470         
44471         this.curve_data.push(newPointGroup);
44472         this.reset();
44473         this.strokeUpdate(e);
44474     },
44475     
44476     strokeUpdate: function(e)
44477     {
44478         var rect = this.canvasEl().dom.getBoundingClientRect();
44479         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44480         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44481         var lastPoints = lastPointGroup.points;
44482         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44483         var isLastPointTooClose = lastPoint
44484             ? point.distanceTo(lastPoint) <= this.min_distance
44485             : false;
44486         var color = lastPointGroup.color;
44487         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44488             var curve = this.addPoint(point);
44489             if (!lastPoint) {
44490                 this.drawDot({color: color, point: point});
44491             }
44492             else if (curve) {
44493                 this.drawCurve({color: color, curve: curve});
44494             }
44495             lastPoints.push({
44496                 time: point.time,
44497                 x: point.x,
44498                 y: point.y
44499             });
44500         }
44501     },
44502     
44503     strokeEnd: function(e)
44504     {
44505         this.strokeUpdate(e);
44506         if (typeof this.onEnd === 'function') {
44507             this.onEnd(e);
44508         }
44509     },
44510     
44511     addPoint:  function (point) {
44512         var _lastPoints = this._lastPoints;
44513         _lastPoints.push(point);
44514         if (_lastPoints.length > 2) {
44515             if (_lastPoints.length === 3) {
44516                 _lastPoints.unshift(_lastPoints[0]);
44517             }
44518             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44519             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44520             _lastPoints.shift();
44521             return curve;
44522         }
44523         return null;
44524     },
44525     
44526     calculateCurveWidths: function (startPoint, endPoint) {
44527         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44528             (1 - this.velocity_filter_weight) * this._lastVelocity;
44529
44530         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44531         var widths = {
44532             end: newWidth,
44533             start: this._lastWidth
44534         };
44535         
44536         this._lastVelocity = velocity;
44537         this._lastWidth = newWidth;
44538         return widths;
44539     },
44540     
44541     drawDot: function (_a) {
44542         var color = _a.color, point = _a.point;
44543         var ctx = this.canvasElCtx();
44544         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44545         ctx.beginPath();
44546         this.drawCurveSegment(point.x, point.y, width);
44547         ctx.closePath();
44548         ctx.fillStyle = color;
44549         ctx.fill();
44550     },
44551     
44552     drawCurve: function (_a) {
44553         var color = _a.color, curve = _a.curve;
44554         var ctx = this.canvasElCtx();
44555         var widthDelta = curve.endWidth - curve.startWidth;
44556         var drawSteps = Math.floor(curve.length()) * 2;
44557         ctx.beginPath();
44558         ctx.fillStyle = color;
44559         for (var i = 0; i < drawSteps; i += 1) {
44560         var t = i / drawSteps;
44561         var tt = t * t;
44562         var ttt = tt * t;
44563         var u = 1 - t;
44564         var uu = u * u;
44565         var uuu = uu * u;
44566         var x = uuu * curve.startPoint.x;
44567         x += 3 * uu * t * curve.control1.x;
44568         x += 3 * u * tt * curve.control2.x;
44569         x += ttt * curve.endPoint.x;
44570         var y = uuu * curve.startPoint.y;
44571         y += 3 * uu * t * curve.control1.y;
44572         y += 3 * u * tt * curve.control2.y;
44573         y += ttt * curve.endPoint.y;
44574         var width = curve.startWidth + ttt * widthDelta;
44575         this.drawCurveSegment(x, y, width);
44576         }
44577         ctx.closePath();
44578         ctx.fill();
44579     },
44580     
44581     drawCurveSegment: function (x, y, width) {
44582         var ctx = this.canvasElCtx();
44583         ctx.moveTo(x, y);
44584         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44585         this.is_empty = false;
44586     },
44587     
44588     clear: function()
44589     {
44590         var ctx = this.canvasElCtx();
44591         var canvas = this.canvasEl().dom;
44592         ctx.fillStyle = this.bg_color;
44593         ctx.clearRect(0, 0, canvas.width, canvas.height);
44594         ctx.fillRect(0, 0, canvas.width, canvas.height);
44595         this.curve_data = [];
44596         this.reset();
44597         this.is_empty = true;
44598     },
44599     
44600     fileEl: function()
44601     {
44602         return  this.el.select('input',true).first();
44603     },
44604     
44605     canvasEl: function()
44606     {
44607         return this.el.select('canvas',true).first();
44608     },
44609     
44610     canvasElCtx: function()
44611     {
44612         return this.el.select('canvas',true).first().dom.getContext('2d');
44613     },
44614     
44615     getImage: function(type)
44616     {
44617         if(this.is_empty) {
44618             return false;
44619         }
44620         
44621         // encryption ?
44622         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44623     },
44624     
44625     drawFromImage: function(img_src)
44626     {
44627         var img = new Image();
44628         
44629         img.onload = function(){
44630             this.canvasElCtx().drawImage(img, 0, 0);
44631         }.bind(this);
44632         
44633         img.src = img_src;
44634         
44635         this.is_empty = false;
44636     },
44637     
44638     selectImage: function()
44639     {
44640         this.fileEl().dom.click();
44641     },
44642     
44643     uploadImage: function(e)
44644     {
44645         var reader = new FileReader();
44646         
44647         reader.onload = function(e){
44648             var img = new Image();
44649             img.onload = function(){
44650                 this.reset();
44651                 this.canvasElCtx().drawImage(img, 0, 0);
44652             }.bind(this);
44653             img.src = e.target.result;
44654         }.bind(this);
44655         
44656         reader.readAsDataURL(e.target.files[0]);
44657     },
44658     
44659     // Bezier Point Constructor
44660     Point: (function () {
44661         function Point(x, y, time) {
44662             this.x = x;
44663             this.y = y;
44664             this.time = time || Date.now();
44665         }
44666         Point.prototype.distanceTo = function (start) {
44667             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44668         };
44669         Point.prototype.equals = function (other) {
44670             return this.x === other.x && this.y === other.y && this.time === other.time;
44671         };
44672         Point.prototype.velocityFrom = function (start) {
44673             return this.time !== start.time
44674             ? this.distanceTo(start) / (this.time - start.time)
44675             : 0;
44676         };
44677         return Point;
44678     }()),
44679     
44680     
44681     // Bezier Constructor
44682     Bezier: (function () {
44683         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44684             this.startPoint = startPoint;
44685             this.control2 = control2;
44686             this.control1 = control1;
44687             this.endPoint = endPoint;
44688             this.startWidth = startWidth;
44689             this.endWidth = endWidth;
44690         }
44691         Bezier.fromPoints = function (points, widths, scope) {
44692             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44693             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44694             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44695         };
44696         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44697             var dx1 = s1.x - s2.x;
44698             var dy1 = s1.y - s2.y;
44699             var dx2 = s2.x - s3.x;
44700             var dy2 = s2.y - s3.y;
44701             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44702             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44703             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44704             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44705             var dxm = m1.x - m2.x;
44706             var dym = m1.y - m2.y;
44707             var k = l2 / (l1 + l2);
44708             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44709             var tx = s2.x - cm.x;
44710             var ty = s2.y - cm.y;
44711             return {
44712                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44713                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44714             };
44715         };
44716         Bezier.prototype.length = function () {
44717             var steps = 10;
44718             var length = 0;
44719             var px;
44720             var py;
44721             for (var i = 0; i <= steps; i += 1) {
44722                 var t = i / steps;
44723                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44724                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44725                 if (i > 0) {
44726                     var xdiff = cx - px;
44727                     var ydiff = cy - py;
44728                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44729                 }
44730                 px = cx;
44731                 py = cy;
44732             }
44733             return length;
44734         };
44735         Bezier.prototype.point = function (t, start, c1, c2, end) {
44736             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44737             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44738             + (3.0 * c2 * (1.0 - t) * t * t)
44739             + (end * t * t * t);
44740         };
44741         return Bezier;
44742     }()),
44743     
44744     throttleStroke: function(fn, wait) {
44745       if (wait === void 0) { wait = 250; }
44746       var previous = 0;
44747       var timeout = null;
44748       var result;
44749       var storedContext;
44750       var storedArgs;
44751       var later = function () {
44752           previous = Date.now();
44753           timeout = null;
44754           result = fn.apply(storedContext, storedArgs);
44755           if (!timeout) {
44756               storedContext = null;
44757               storedArgs = [];
44758           }
44759       };
44760       return function wrapper() {
44761           var args = [];
44762           for (var _i = 0; _i < arguments.length; _i++) {
44763               args[_i] = arguments[_i];
44764           }
44765           var now = Date.now();
44766           var remaining = wait - (now - previous);
44767           storedContext = this;
44768           storedArgs = args;
44769           if (remaining <= 0 || remaining > wait) {
44770               if (timeout) {
44771                   clearTimeout(timeout);
44772                   timeout = null;
44773               }
44774               previous = now;
44775               result = fn.apply(storedContext, storedArgs);
44776               if (!timeout) {
44777                   storedContext = null;
44778                   storedArgs = [];
44779               }
44780           }
44781           else if (!timeout) {
44782               timeout = window.setTimeout(later, remaining);
44783           }
44784           return result;
44785       };
44786   }
44787   
44788 });
44789
44790  
44791
44792