fine tune buttonuploader api
[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         this.fireEvent('uploaded', this,  this.selectorEl.dom.files );
2978         
2979     },
2980     
2981        
2982    
2983     
2984     /**
2985      * addCard - add an Attachment to the uploader
2986      * @param data - the data about the image to upload
2987      *
2988      * {
2989           id : 123
2990           title : "Title of file",
2991           is_uploaded : false,
2992           src : "http://.....",
2993           srcfile : { the File upload object },
2994           mimetype : file.type,
2995           preview : false,
2996           is_deleted : 0
2997           .. any other data...
2998         }
2999      *
3000      * 
3001     */
3002      
3003     reset: function()
3004     {
3005          
3006          this.selectorEl
3007     } 
3008     
3009     
3010     
3011     
3012 });
3013  /*
3014  * - LGPL
3015  *
3016  * image
3017  * 
3018  */
3019
3020
3021 /**
3022  * @class Roo.bootstrap.Img
3023  * @extends Roo.bootstrap.Component
3024  * Bootstrap Img class
3025  * @cfg {Boolean} imgResponsive false | true
3026  * @cfg {String} border rounded | circle | thumbnail
3027  * @cfg {String} src image source
3028  * @cfg {String} alt image alternative text
3029  * @cfg {String} href a tag href
3030  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3031  * @cfg {String} xsUrl xs image source
3032  * @cfg {String} smUrl sm image source
3033  * @cfg {String} mdUrl md image source
3034  * @cfg {String} lgUrl lg image source
3035  * 
3036  * @constructor
3037  * Create a new Input
3038  * @param {Object} config The config object
3039  */
3040
3041 Roo.bootstrap.Img = function(config){
3042     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3043     
3044     this.addEvents({
3045         // img events
3046         /**
3047          * @event click
3048          * The img click event for the img.
3049          * @param {Roo.EventObject} e
3050          */
3051         "click" : true
3052     });
3053 };
3054
3055 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3056     
3057     imgResponsive: true,
3058     border: '',
3059     src: 'about:blank',
3060     href: false,
3061     target: false,
3062     xsUrl: '',
3063     smUrl: '',
3064     mdUrl: '',
3065     lgUrl: '',
3066
3067     getAutoCreate : function()
3068     {   
3069         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3070             return this.createSingleImg();
3071         }
3072         
3073         var cfg = {
3074             tag: 'div',
3075             cls: 'roo-image-responsive-group',
3076             cn: []
3077         };
3078         var _this = this;
3079         
3080         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3081             
3082             if(!_this[size + 'Url']){
3083                 return;
3084             }
3085             
3086             var img = {
3087                 tag: 'img',
3088                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3089                 html: _this.html || cfg.html,
3090                 src: _this[size + 'Url']
3091             };
3092             
3093             img.cls += ' roo-image-responsive-' + size;
3094             
3095             var s = ['xs', 'sm', 'md', 'lg'];
3096             
3097             s.splice(s.indexOf(size), 1);
3098             
3099             Roo.each(s, function(ss){
3100                 img.cls += ' hidden-' + ss;
3101             });
3102             
3103             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3104                 cfg.cls += ' img-' + _this.border;
3105             }
3106             
3107             if(_this.alt){
3108                 cfg.alt = _this.alt;
3109             }
3110             
3111             if(_this.href){
3112                 var a = {
3113                     tag: 'a',
3114                     href: _this.href,
3115                     cn: [
3116                         img
3117                     ]
3118                 };
3119
3120                 if(this.target){
3121                     a.target = _this.target;
3122                 }
3123             }
3124             
3125             cfg.cn.push((_this.href) ? a : img);
3126             
3127         });
3128         
3129         return cfg;
3130     },
3131     
3132     createSingleImg : function()
3133     {
3134         var cfg = {
3135             tag: 'img',
3136             cls: (this.imgResponsive) ? 'img-responsive' : '',
3137             html : null,
3138             src : 'about:blank'  // just incase src get's set to undefined?!?
3139         };
3140         
3141         cfg.html = this.html || cfg.html;
3142         
3143         cfg.src = this.src || cfg.src;
3144         
3145         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3146             cfg.cls += ' img-' + this.border;
3147         }
3148         
3149         if(this.alt){
3150             cfg.alt = this.alt;
3151         }
3152         
3153         if(this.href){
3154             var a = {
3155                 tag: 'a',
3156                 href: this.href,
3157                 cn: [
3158                     cfg
3159                 ]
3160             };
3161             
3162             if(this.target){
3163                 a.target = this.target;
3164             }
3165             
3166         }
3167         
3168         return (this.href) ? a : cfg;
3169     },
3170     
3171     initEvents: function() 
3172     {
3173         if(!this.href){
3174             this.el.on('click', this.onClick, this);
3175         }
3176         
3177     },
3178     
3179     onClick : function(e)
3180     {
3181         Roo.log('img onclick');
3182         this.fireEvent('click', this, e);
3183     },
3184     /**
3185      * Sets the url of the image - used to update it
3186      * @param {String} url the url of the image
3187      */
3188     
3189     setSrc : function(url)
3190     {
3191         this.src =  url;
3192         
3193         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3194             this.el.dom.src =  url;
3195             return;
3196         }
3197         
3198         this.el.select('img', true).first().dom.src =  url;
3199     }
3200     
3201     
3202    
3203 });
3204
3205  /*
3206  * - LGPL
3207  *
3208  * image
3209  * 
3210  */
3211
3212
3213 /**
3214  * @class Roo.bootstrap.Link
3215  * @extends Roo.bootstrap.Component
3216  * Bootstrap Link Class
3217  * @cfg {String} alt image alternative text
3218  * @cfg {String} href a tag href
3219  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3220  * @cfg {String} html the content of the link.
3221  * @cfg {String} anchor name for the anchor link
3222  * @cfg {String} fa - favicon
3223
3224  * @cfg {Boolean} preventDefault (true | false) default false
3225
3226  * 
3227  * @constructor
3228  * Create a new Input
3229  * @param {Object} config The config object
3230  */
3231
3232 Roo.bootstrap.Link = function(config){
3233     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3234     
3235     this.addEvents({
3236         // img events
3237         /**
3238          * @event click
3239          * The img click event for the img.
3240          * @param {Roo.EventObject} e
3241          */
3242         "click" : true
3243     });
3244 };
3245
3246 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3247     
3248     href: false,
3249     target: false,
3250     preventDefault: false,
3251     anchor : false,
3252     alt : false,
3253     fa: false,
3254
3255
3256     getAutoCreate : function()
3257     {
3258         var html = this.html || '';
3259         
3260         if (this.fa !== false) {
3261             html = '<i class="fa fa-' + this.fa + '"></i>';
3262         }
3263         var cfg = {
3264             tag: 'a'
3265         };
3266         // anchor's do not require html/href...
3267         if (this.anchor === false) {
3268             cfg.html = html;
3269             cfg.href = this.href || '#';
3270         } else {
3271             cfg.name = this.anchor;
3272             if (this.html !== false || this.fa !== false) {
3273                 cfg.html = html;
3274             }
3275             if (this.href !== false) {
3276                 cfg.href = this.href;
3277             }
3278         }
3279         
3280         if(this.alt !== false){
3281             cfg.alt = this.alt;
3282         }
3283         
3284         
3285         if(this.target !== false) {
3286             cfg.target = this.target;
3287         }
3288         
3289         return cfg;
3290     },
3291     
3292     initEvents: function() {
3293         
3294         if(!this.href || this.preventDefault){
3295             this.el.on('click', this.onClick, this);
3296         }
3297     },
3298     
3299     onClick : function(e)
3300     {
3301         if(this.preventDefault){
3302             e.preventDefault();
3303         }
3304         //Roo.log('img onclick');
3305         this.fireEvent('click', this, e);
3306     }
3307    
3308 });
3309
3310  /*
3311  * - LGPL
3312  *
3313  * header
3314  * 
3315  */
3316
3317 /**
3318  * @class Roo.bootstrap.Header
3319  * @extends Roo.bootstrap.Component
3320  * Bootstrap Header class
3321  * @cfg {String} html content of header
3322  * @cfg {Number} level (1|2|3|4|5|6) default 1
3323  * 
3324  * @constructor
3325  * Create a new Header
3326  * @param {Object} config The config object
3327  */
3328
3329
3330 Roo.bootstrap.Header  = function(config){
3331     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3332 };
3333
3334 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3335     
3336     //href : false,
3337     html : false,
3338     level : 1,
3339     
3340     
3341     
3342     getAutoCreate : function(){
3343         
3344         
3345         
3346         var cfg = {
3347             tag: 'h' + (1 *this.level),
3348             html: this.html || ''
3349         } ;
3350         
3351         return cfg;
3352     }
3353    
3354 });
3355
3356  
3357
3358  /*
3359  * Based on:
3360  * Ext JS Library 1.1.1
3361  * Copyright(c) 2006-2007, Ext JS, LLC.
3362  *
3363  * Originally Released Under LGPL - original licence link has changed is not relivant.
3364  *
3365  * Fork - LGPL
3366  * <script type="text/javascript">
3367  */
3368  
3369 /**
3370  * @class Roo.bootstrap.MenuMgr
3371  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3372  * @singleton
3373  */
3374 Roo.bootstrap.MenuMgr = function(){
3375    var menus, active, groups = {}, attached = false, lastShow = new Date();
3376
3377    // private - called when first menu is created
3378    function init(){
3379        menus = {};
3380        active = new Roo.util.MixedCollection();
3381        Roo.get(document).addKeyListener(27, function(){
3382            if(active.length > 0){
3383                hideAll();
3384            }
3385        });
3386    }
3387
3388    // private
3389    function hideAll(){
3390        if(active && active.length > 0){
3391            var c = active.clone();
3392            c.each(function(m){
3393                m.hide();
3394            });
3395        }
3396    }
3397
3398    // private
3399    function onHide(m){
3400        active.remove(m);
3401        if(active.length < 1){
3402            Roo.get(document).un("mouseup", onMouseDown);
3403             
3404            attached = false;
3405        }
3406    }
3407
3408    // private
3409    function onShow(m){
3410        var last = active.last();
3411        lastShow = new Date();
3412        active.add(m);
3413        if(!attached){
3414           Roo.get(document).on("mouseup", onMouseDown);
3415            
3416            attached = true;
3417        }
3418        if(m.parentMenu){
3419           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3420           m.parentMenu.activeChild = m;
3421        }else if(last && last.isVisible()){
3422           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3423        }
3424    }
3425
3426    // private
3427    function onBeforeHide(m){
3428        if(m.activeChild){
3429            m.activeChild.hide();
3430        }
3431        if(m.autoHideTimer){
3432            clearTimeout(m.autoHideTimer);
3433            delete m.autoHideTimer;
3434        }
3435    }
3436
3437    // private
3438    function onBeforeShow(m){
3439        var pm = m.parentMenu;
3440        if(!pm && !m.allowOtherMenus){
3441            hideAll();
3442        }else if(pm && pm.activeChild && active != m){
3443            pm.activeChild.hide();
3444        }
3445    }
3446
3447    // private this should really trigger on mouseup..
3448    function onMouseDown(e){
3449         Roo.log("on Mouse Up");
3450         
3451         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3452             Roo.log("MenuManager hideAll");
3453             hideAll();
3454             e.stopEvent();
3455         }
3456         
3457         
3458    }
3459
3460    // private
3461    function onBeforeCheck(mi, state){
3462        if(state){
3463            var g = groups[mi.group];
3464            for(var i = 0, l = g.length; i < l; i++){
3465                if(g[i] != mi){
3466                    g[i].setChecked(false);
3467                }
3468            }
3469        }
3470    }
3471
3472    return {
3473
3474        /**
3475         * Hides all menus that are currently visible
3476         */
3477        hideAll : function(){
3478             hideAll();  
3479        },
3480
3481        // private
3482        register : function(menu){
3483            if(!menus){
3484                init();
3485            }
3486            menus[menu.id] = menu;
3487            menu.on("beforehide", onBeforeHide);
3488            menu.on("hide", onHide);
3489            menu.on("beforeshow", onBeforeShow);
3490            menu.on("show", onShow);
3491            var g = menu.group;
3492            if(g && menu.events["checkchange"]){
3493                if(!groups[g]){
3494                    groups[g] = [];
3495                }
3496                groups[g].push(menu);
3497                menu.on("checkchange", onCheck);
3498            }
3499        },
3500
3501         /**
3502          * Returns a {@link Roo.menu.Menu} object
3503          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3504          * be used to generate and return a new Menu instance.
3505          */
3506        get : function(menu){
3507            if(typeof menu == "string"){ // menu id
3508                return menus[menu];
3509            }else if(menu.events){  // menu instance
3510                return menu;
3511            }
3512            /*else if(typeof menu.length == 'number'){ // array of menu items?
3513                return new Roo.bootstrap.Menu({items:menu});
3514            }else{ // otherwise, must be a config
3515                return new Roo.bootstrap.Menu(menu);
3516            }
3517            */
3518            return false;
3519        },
3520
3521        // private
3522        unregister : function(menu){
3523            delete menus[menu.id];
3524            menu.un("beforehide", onBeforeHide);
3525            menu.un("hide", onHide);
3526            menu.un("beforeshow", onBeforeShow);
3527            menu.un("show", onShow);
3528            var g = menu.group;
3529            if(g && menu.events["checkchange"]){
3530                groups[g].remove(menu);
3531                menu.un("checkchange", onCheck);
3532            }
3533        },
3534
3535        // private
3536        registerCheckable : function(menuItem){
3537            var g = menuItem.group;
3538            if(g){
3539                if(!groups[g]){
3540                    groups[g] = [];
3541                }
3542                groups[g].push(menuItem);
3543                menuItem.on("beforecheckchange", onBeforeCheck);
3544            }
3545        },
3546
3547        // private
3548        unregisterCheckable : function(menuItem){
3549            var g = menuItem.group;
3550            if(g){
3551                groups[g].remove(menuItem);
3552                menuItem.un("beforecheckchange", onBeforeCheck);
3553            }
3554        }
3555    };
3556 }();/*
3557  * - LGPL
3558  *
3559  * menu
3560  * 
3561  */
3562
3563 /**
3564  * @class Roo.bootstrap.Menu
3565  * @extends Roo.bootstrap.Component
3566  * Bootstrap Menu class - container for MenuItems
3567  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3568  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3569  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3570  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3571  * 
3572  * @constructor
3573  * Create a new Menu
3574  * @param {Object} config The config object
3575  */
3576
3577
3578 Roo.bootstrap.Menu = function(config){
3579     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3580     if (this.registerMenu && this.type != 'treeview')  {
3581         Roo.bootstrap.MenuMgr.register(this);
3582     }
3583     
3584     
3585     this.addEvents({
3586         /**
3587          * @event beforeshow
3588          * Fires before this menu is displayed (return false to block)
3589          * @param {Roo.menu.Menu} this
3590          */
3591         beforeshow : true,
3592         /**
3593          * @event beforehide
3594          * Fires before this menu is hidden (return false to block)
3595          * @param {Roo.menu.Menu} this
3596          */
3597         beforehide : true,
3598         /**
3599          * @event show
3600          * Fires after this menu is displayed
3601          * @param {Roo.menu.Menu} this
3602          */
3603         show : true,
3604         /**
3605          * @event hide
3606          * Fires after this menu is hidden
3607          * @param {Roo.menu.Menu} this
3608          */
3609         hide : true,
3610         /**
3611          * @event click
3612          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3613          * @param {Roo.menu.Menu} this
3614          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3615          * @param {Roo.EventObject} e
3616          */
3617         click : true,
3618         /**
3619          * @event mouseover
3620          * Fires when the mouse is hovering over this menu
3621          * @param {Roo.menu.Menu} this
3622          * @param {Roo.EventObject} e
3623          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3624          */
3625         mouseover : true,
3626         /**
3627          * @event mouseout
3628          * Fires when the mouse exits this menu
3629          * @param {Roo.menu.Menu} this
3630          * @param {Roo.EventObject} e
3631          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3632          */
3633         mouseout : true,
3634         /**
3635          * @event itemclick
3636          * Fires when a menu item contained in this menu is clicked
3637          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3638          * @param {Roo.EventObject} e
3639          */
3640         itemclick: true
3641     });
3642     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3643 };
3644
3645 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3646     
3647    /// html : false,
3648     //align : '',
3649     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3650     type: false,
3651     /**
3652      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3653      */
3654     registerMenu : true,
3655     
3656     menuItems :false, // stores the menu items..
3657     
3658     hidden:true,
3659         
3660     parentMenu : false,
3661     
3662     stopEvent : true,
3663     
3664     isLink : false,
3665     
3666     getChildContainer : function() {
3667         return this.el;  
3668     },
3669     
3670     getAutoCreate : function(){
3671          
3672         //if (['right'].indexOf(this.align)!==-1) {
3673         //    cfg.cn[1].cls += ' pull-right'
3674         //}
3675         
3676         
3677         var cfg = {
3678             tag : 'ul',
3679             cls : 'dropdown-menu' ,
3680             style : 'z-index:1000'
3681             
3682         };
3683         
3684         if (this.type === 'submenu') {
3685             cfg.cls = 'submenu active';
3686         }
3687         if (this.type === 'treeview') {
3688             cfg.cls = 'treeview-menu';
3689         }
3690         
3691         return cfg;
3692     },
3693     initEvents : function() {
3694         
3695        // Roo.log("ADD event");
3696        // Roo.log(this.triggerEl.dom);
3697         
3698         this.triggerEl.on('click', this.onTriggerClick, this);
3699         
3700         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3701         
3702         
3703         if (this.triggerEl.hasClass('nav-item')) {
3704             // dropdown toggle on the 'a' in BS4?
3705             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3706         } else {
3707             this.triggerEl.addClass('dropdown-toggle');
3708         }
3709         if (Roo.isTouch) {
3710             this.el.on('touchstart'  , this.onTouch, this);
3711         }
3712         this.el.on('click' , this.onClick, this);
3713
3714         this.el.on("mouseover", this.onMouseOver, this);
3715         this.el.on("mouseout", this.onMouseOut, this);
3716         
3717     },
3718     
3719     findTargetItem : function(e)
3720     {
3721         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3722         if(!t){
3723             return false;
3724         }
3725         //Roo.log(t);         Roo.log(t.id);
3726         if(t && t.id){
3727             //Roo.log(this.menuitems);
3728             return this.menuitems.get(t.id);
3729             
3730             //return this.items.get(t.menuItemId);
3731         }
3732         
3733         return false;
3734     },
3735     
3736     onTouch : function(e) 
3737     {
3738         Roo.log("menu.onTouch");
3739         //e.stopEvent(); this make the user popdown broken
3740         this.onClick(e);
3741     },
3742     
3743     onClick : function(e)
3744     {
3745         Roo.log("menu.onClick");
3746         
3747         var t = this.findTargetItem(e);
3748         if(!t || t.isContainer){
3749             return;
3750         }
3751         Roo.log(e);
3752         /*
3753         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3754             if(t == this.activeItem && t.shouldDeactivate(e)){
3755                 this.activeItem.deactivate();
3756                 delete this.activeItem;
3757                 return;
3758             }
3759             if(t.canActivate){
3760                 this.setActiveItem(t, true);
3761             }
3762             return;
3763             
3764             
3765         }
3766         */
3767        
3768         Roo.log('pass click event');
3769         
3770         t.onClick(e);
3771         
3772         this.fireEvent("click", this, t, e);
3773         
3774         var _this = this;
3775         
3776         if(!t.href.length || t.href == '#'){
3777             (function() { _this.hide(); }).defer(100);
3778         }
3779         
3780     },
3781     
3782     onMouseOver : function(e){
3783         var t  = this.findTargetItem(e);
3784         //Roo.log(t);
3785         //if(t){
3786         //    if(t.canActivate && !t.disabled){
3787         //        this.setActiveItem(t, true);
3788         //    }
3789         //}
3790         
3791         this.fireEvent("mouseover", this, e, t);
3792     },
3793     isVisible : function(){
3794         return !this.hidden;
3795     },
3796     onMouseOut : function(e){
3797         var t  = this.findTargetItem(e);
3798         
3799         //if(t ){
3800         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3801         //        this.activeItem.deactivate();
3802         //        delete this.activeItem;
3803         //    }
3804         //}
3805         this.fireEvent("mouseout", this, e, t);
3806     },
3807     
3808     
3809     /**
3810      * Displays this menu relative to another element
3811      * @param {String/HTMLElement/Roo.Element} element The element to align to
3812      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3813      * the element (defaults to this.defaultAlign)
3814      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3815      */
3816     show : function(el, pos, parentMenu)
3817     {
3818         if (false === this.fireEvent("beforeshow", this)) {
3819             Roo.log("show canceled");
3820             return;
3821         }
3822         this.parentMenu = parentMenu;
3823         if(!this.el){
3824             this.render();
3825         }
3826         
3827         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3828     },
3829      /**
3830      * Displays this menu at a specific xy position
3831      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3832      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3833      */
3834     showAt : function(xy, parentMenu, /* private: */_e){
3835         this.parentMenu = parentMenu;
3836         if(!this.el){
3837             this.render();
3838         }
3839         if(_e !== false){
3840             this.fireEvent("beforeshow", this);
3841             //xy = this.el.adjustForConstraints(xy);
3842         }
3843         
3844         //this.el.show();
3845         this.hideMenuItems();
3846         this.hidden = false;
3847         this.triggerEl.addClass('open');
3848         this.el.addClass('show');
3849         
3850         // reassign x when hitting right
3851         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3852             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3853         }
3854         
3855         // reassign y when hitting bottom
3856         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3857             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3858         }
3859         
3860         // but the list may align on trigger left or trigger top... should it be a properity?
3861         
3862         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3863             this.el.setXY(xy);
3864         }
3865         
3866         this.focus();
3867         this.fireEvent("show", this);
3868     },
3869     
3870     focus : function(){
3871         return;
3872         if(!this.hidden){
3873             this.doFocus.defer(50, this);
3874         }
3875     },
3876
3877     doFocus : function(){
3878         if(!this.hidden){
3879             this.focusEl.focus();
3880         }
3881     },
3882
3883     /**
3884      * Hides this menu and optionally all parent menus
3885      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3886      */
3887     hide : function(deep)
3888     {
3889         if (false === this.fireEvent("beforehide", this)) {
3890             Roo.log("hide canceled");
3891             return;
3892         }
3893         this.hideMenuItems();
3894         if(this.el && this.isVisible()){
3895            
3896             if(this.activeItem){
3897                 this.activeItem.deactivate();
3898                 this.activeItem = null;
3899             }
3900             this.triggerEl.removeClass('open');;
3901             this.el.removeClass('show');
3902             this.hidden = true;
3903             this.fireEvent("hide", this);
3904         }
3905         if(deep === true && this.parentMenu){
3906             this.parentMenu.hide(true);
3907         }
3908     },
3909     
3910     onTriggerClick : function(e)
3911     {
3912         Roo.log('trigger click');
3913         
3914         var target = e.getTarget();
3915         
3916         Roo.log(target.nodeName.toLowerCase());
3917         
3918         if(target.nodeName.toLowerCase() === 'i'){
3919             e.preventDefault();
3920         }
3921         
3922     },
3923     
3924     onTriggerPress  : function(e)
3925     {
3926         Roo.log('trigger press');
3927         //Roo.log(e.getTarget());
3928        // Roo.log(this.triggerEl.dom);
3929        
3930         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3931         var pel = Roo.get(e.getTarget());
3932         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3933             Roo.log('is treeview or dropdown?');
3934             return;
3935         }
3936         
3937         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3938             return;
3939         }
3940         
3941         if (this.isVisible()) {
3942             Roo.log('hide');
3943             this.hide();
3944         } else {
3945             Roo.log('show');
3946             this.show(this.triggerEl, '?', false);
3947         }
3948         
3949         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3950             e.stopEvent();
3951         }
3952         
3953     },
3954        
3955     
3956     hideMenuItems : function()
3957     {
3958         Roo.log("hide Menu Items");
3959         if (!this.el) { 
3960             return;
3961         }
3962         
3963         this.el.select('.open',true).each(function(aa) {
3964             
3965             aa.removeClass('open');
3966          
3967         });
3968     },
3969     addxtypeChild : function (tree, cntr) {
3970         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3971           
3972         this.menuitems.add(comp);
3973         return comp;
3974
3975     },
3976     getEl : function()
3977     {
3978         Roo.log(this.el);
3979         return this.el;
3980     },
3981     
3982     clear : function()
3983     {
3984         this.getEl().dom.innerHTML = '';
3985         this.menuitems.clear();
3986     }
3987 });
3988
3989  
3990  /*
3991  * - LGPL
3992  *
3993  * menu item
3994  * 
3995  */
3996
3997
3998 /**
3999  * @class Roo.bootstrap.MenuItem
4000  * @extends Roo.bootstrap.Component
4001  * Bootstrap MenuItem class
4002  * @cfg {String} html the menu label
4003  * @cfg {String} href the link
4004  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4005  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4006  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4007  * @cfg {String} fa favicon to show on left of menu item.
4008  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4009  * 
4010  * 
4011  * @constructor
4012  * Create a new MenuItem
4013  * @param {Object} config The config object
4014  */
4015
4016
4017 Roo.bootstrap.MenuItem = function(config){
4018     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4019     this.addEvents({
4020         // raw events
4021         /**
4022          * @event click
4023          * The raw click event for the entire grid.
4024          * @param {Roo.bootstrap.MenuItem} this
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true
4028     });
4029 };
4030
4031 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4032     
4033     href : false,
4034     html : false,
4035     preventDefault: false,
4036     isContainer : false,
4037     active : false,
4038     fa: false,
4039     
4040     getAutoCreate : function(){
4041         
4042         if(this.isContainer){
4043             return {
4044                 tag: 'li',
4045                 cls: 'dropdown-menu-item '
4046             };
4047         }
4048         var ctag = {
4049             tag: 'span',
4050             html: 'Link'
4051         };
4052         
4053         var anc = {
4054             tag : 'a',
4055             cls : 'dropdown-item',
4056             href : '#',
4057             cn : [  ]
4058         };
4059         
4060         if (this.fa !== false) {
4061             anc.cn.push({
4062                 tag : 'i',
4063                 cls : 'fa fa-' + this.fa
4064             });
4065         }
4066         
4067         anc.cn.push(ctag);
4068         
4069         
4070         var cfg= {
4071             tag: 'li',
4072             cls: 'dropdown-menu-item',
4073             cn: [ anc ]
4074         };
4075         if (this.parent().type == 'treeview') {
4076             cfg.cls = 'treeview-menu';
4077         }
4078         if (this.active) {
4079             cfg.cls += ' active';
4080         }
4081         
4082         
4083         
4084         anc.href = this.href || cfg.cn[0].href ;
4085         ctag.html = this.html || cfg.cn[0].html ;
4086         return cfg;
4087     },
4088     
4089     initEvents: function()
4090     {
4091         if (this.parent().type == 'treeview') {
4092             this.el.select('a').on('click', this.onClick, this);
4093         }
4094         
4095         if (this.menu) {
4096             this.menu.parentType = this.xtype;
4097             this.menu.triggerEl = this.el;
4098             this.menu = this.addxtype(Roo.apply({}, this.menu));
4099         }
4100         
4101     },
4102     onClick : function(e)
4103     {
4104         Roo.log('item on click ');
4105         
4106         if(this.preventDefault){
4107             e.preventDefault();
4108         }
4109         //this.parent().hideMenuItems();
4110         
4111         this.fireEvent('click', this, e);
4112     },
4113     getEl : function()
4114     {
4115         return this.el;
4116     } 
4117 });
4118
4119  
4120
4121  /*
4122  * - LGPL
4123  *
4124  * menu separator
4125  * 
4126  */
4127
4128
4129 /**
4130  * @class Roo.bootstrap.MenuSeparator
4131  * @extends Roo.bootstrap.Component
4132  * Bootstrap MenuSeparator class
4133  * 
4134  * @constructor
4135  * Create a new MenuItem
4136  * @param {Object} config The config object
4137  */
4138
4139
4140 Roo.bootstrap.MenuSeparator = function(config){
4141     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4142 };
4143
4144 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4145     
4146     getAutoCreate : function(){
4147         var cfg = {
4148             cls: 'divider',
4149             tag : 'li'
4150         };
4151         
4152         return cfg;
4153     }
4154    
4155 });
4156
4157  
4158
4159  
4160 /*
4161 * Licence: LGPL
4162 */
4163
4164 /**
4165  * @class Roo.bootstrap.Modal
4166  * @extends Roo.bootstrap.Component
4167  * Bootstrap Modal class
4168  * @cfg {String} title Title of dialog
4169  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4170  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4171  * @cfg {Boolean} specificTitle default false
4172  * @cfg {Array} buttons Array of buttons or standard button set..
4173  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4174  * @cfg {Boolean} animate default true
4175  * @cfg {Boolean} allow_close default true
4176  * @cfg {Boolean} fitwindow default false
4177  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4178  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4179  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4180  * @cfg {String} size (sm|lg|xl) default empty
4181  * @cfg {Number} max_width set the max width of modal
4182  * @cfg {Boolean} editableTitle can the title be edited
4183
4184  *
4185  *
4186  * @constructor
4187  * Create a new Modal Dialog
4188  * @param {Object} config The config object
4189  */
4190
4191 Roo.bootstrap.Modal = function(config){
4192     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4193     this.addEvents({
4194         // raw events
4195         /**
4196          * @event btnclick
4197          * The raw btnclick event for the button
4198          * @param {Roo.EventObject} e
4199          */
4200         "btnclick" : true,
4201         /**
4202          * @event resize
4203          * Fire when dialog resize
4204          * @param {Roo.bootstrap.Modal} this
4205          * @param {Roo.EventObject} e
4206          */
4207         "resize" : true,
4208         /**
4209          * @event titlechanged
4210          * Fire when the editable title has been changed
4211          * @param {Roo.bootstrap.Modal} this
4212          * @param {Roo.EventObject} value
4213          */
4214         "titlechanged" : true 
4215         
4216     });
4217     this.buttons = this.buttons || [];
4218
4219     if (this.tmpl) {
4220         this.tmpl = Roo.factory(this.tmpl);
4221     }
4222
4223 };
4224
4225 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4226
4227     title : 'test dialog',
4228
4229     buttons : false,
4230
4231     // set on load...
4232
4233     html: false,
4234
4235     tmp: false,
4236
4237     specificTitle: false,
4238
4239     buttonPosition: 'right',
4240
4241     allow_close : true,
4242
4243     animate : true,
4244
4245     fitwindow: false,
4246     
4247      // private
4248     dialogEl: false,
4249     bodyEl:  false,
4250     footerEl:  false,
4251     titleEl:  false,
4252     closeEl:  false,
4253
4254     size: '',
4255     
4256     max_width: 0,
4257     
4258     max_height: 0,
4259     
4260     fit_content: false,
4261     editableTitle  : false,
4262
4263     onRender : function(ct, position)
4264     {
4265         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4266
4267         if(!this.el){
4268             var cfg = Roo.apply({},  this.getAutoCreate());
4269             cfg.id = Roo.id();
4270             //if(!cfg.name){
4271             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4272             //}
4273             //if (!cfg.name.length) {
4274             //    delete cfg.name;
4275            // }
4276             if (this.cls) {
4277                 cfg.cls += ' ' + this.cls;
4278             }
4279             if (this.style) {
4280                 cfg.style = this.style;
4281             }
4282             this.el = Roo.get(document.body).createChild(cfg, position);
4283         }
4284         //var type = this.el.dom.type;
4285
4286
4287         if(this.tabIndex !== undefined){
4288             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4289         }
4290
4291         this.dialogEl = this.el.select('.modal-dialog',true).first();
4292         this.bodyEl = this.el.select('.modal-body',true).first();
4293         this.closeEl = this.el.select('.modal-header .close', true).first();
4294         this.headerEl = this.el.select('.modal-header',true).first();
4295         this.titleEl = this.el.select('.modal-title',true).first();
4296         this.footerEl = this.el.select('.modal-footer',true).first();
4297
4298         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4299         
4300         //this.el.addClass("x-dlg-modal");
4301
4302         if (this.buttons.length) {
4303             Roo.each(this.buttons, function(bb) {
4304                 var b = Roo.apply({}, bb);
4305                 b.xns = b.xns || Roo.bootstrap;
4306                 b.xtype = b.xtype || 'Button';
4307                 if (typeof(b.listeners) == 'undefined') {
4308                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4309                 }
4310
4311                 var btn = Roo.factory(b);
4312
4313                 btn.render(this.getButtonContainer());
4314
4315             },this);
4316         }
4317         // render the children.
4318         var nitems = [];
4319
4320         if(typeof(this.items) != 'undefined'){
4321             var items = this.items;
4322             delete this.items;
4323
4324             for(var i =0;i < items.length;i++) {
4325                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4326             }
4327         }
4328
4329         this.items = nitems;
4330
4331         // where are these used - they used to be body/close/footer
4332
4333
4334         this.initEvents();
4335         //this.el.addClass([this.fieldClass, this.cls]);
4336
4337     },
4338
4339     getAutoCreate : function()
4340     {
4341         // we will default to modal-body-overflow - might need to remove or make optional later.
4342         var bdy = {
4343                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4344                 html : this.html || ''
4345         };
4346
4347         var title = {
4348             tag: 'h5',
4349             cls : 'modal-title',
4350             html : this.title
4351         };
4352
4353         if(this.specificTitle){ // WTF is this?
4354             title = this.title;
4355         }
4356
4357         var header = [];
4358         if (this.allow_close && Roo.bootstrap.version == 3) {
4359             header.push({
4360                 tag: 'button',
4361                 cls : 'close',
4362                 html : '&times'
4363             });
4364         }
4365
4366         header.push(title);
4367
4368         if (this.editableTitle) {
4369             header.push({
4370                 cls: 'form-control roo-editable-title d-none',
4371                 tag: 'input',
4372                 type: 'text'
4373             });
4374         }
4375         
4376         if (this.allow_close && Roo.bootstrap.version == 4) {
4377             header.push({
4378                 tag: 'button',
4379                 cls : 'close',
4380                 html : '&times'
4381             });
4382         }
4383         
4384         var size = '';
4385
4386         if(this.size.length){
4387             size = 'modal-' + this.size;
4388         }
4389         
4390         var footer = Roo.bootstrap.version == 3 ?
4391             {
4392                 cls : 'modal-footer',
4393                 cn : [
4394                     {
4395                         tag: 'div',
4396                         cls: 'btn-' + this.buttonPosition
4397                     }
4398                 ]
4399
4400             } :
4401             {  // BS4 uses mr-auto on left buttons....
4402                 cls : 'modal-footer'
4403             };
4404
4405             
4406
4407         
4408         
4409         var modal = {
4410             cls: "modal",
4411              cn : [
4412                 {
4413                     cls: "modal-dialog " + size,
4414                     cn : [
4415                         {
4416                             cls : "modal-content",
4417                             cn : [
4418                                 {
4419                                     cls : 'modal-header',
4420                                     cn : header
4421                                 },
4422                                 bdy,
4423                                 footer
4424                             ]
4425
4426                         }
4427                     ]
4428
4429                 }
4430             ]
4431         };
4432
4433         if(this.animate){
4434             modal.cls += ' fade';
4435         }
4436
4437         return modal;
4438
4439     },
4440     getChildContainer : function() {
4441
4442          return this.bodyEl;
4443
4444     },
4445     getButtonContainer : function() {
4446         
4447          return Roo.bootstrap.version == 4 ?
4448             this.el.select('.modal-footer',true).first()
4449             : this.el.select('.modal-footer div',true).first();
4450
4451     },
4452     initEvents : function()
4453     {
4454         if (this.allow_close) {
4455             this.closeEl.on('click', this.hide, this);
4456         }
4457         Roo.EventManager.onWindowResize(this.resize, this, true);
4458         if (this.editableTitle) {
4459             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4460             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4461             this.headerEditEl.on('keyup', function(e) {
4462                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4463                         this.toggleHeaderInput(false)
4464                     }
4465                 }, this);
4466             this.headerEditEl.on('blur', function(e) {
4467                 this.toggleHeaderInput(false)
4468             },this);
4469         }
4470
4471     },
4472   
4473
4474     resize : function()
4475     {
4476         this.maskEl.setSize(
4477             Roo.lib.Dom.getViewWidth(true),
4478             Roo.lib.Dom.getViewHeight(true)
4479         );
4480         
4481         if (this.fitwindow) {
4482             
4483            this.dialogEl.setStyle( { 'max-width' : '100%' });
4484             this.setSize(
4485                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4486                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4487             );
4488             return;
4489         }
4490         
4491         if(this.max_width !== 0) {
4492             
4493             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4494             
4495             if(this.height) {
4496                 this.setSize(w, this.height);
4497                 return;
4498             }
4499             
4500             if(this.max_height) {
4501                 this.setSize(w,Math.min(
4502                     this.max_height,
4503                     Roo.lib.Dom.getViewportHeight(true) - 60
4504                 ));
4505                 
4506                 return;
4507             }
4508             
4509             if(!this.fit_content) {
4510                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4511                 return;
4512             }
4513             
4514             this.setSize(w, Math.min(
4515                 60 +
4516                 this.headerEl.getHeight() + 
4517                 this.footerEl.getHeight() + 
4518                 this.getChildHeight(this.bodyEl.dom.childNodes),
4519                 Roo.lib.Dom.getViewportHeight(true) - 60)
4520             );
4521         }
4522         
4523     },
4524
4525     setSize : function(w,h)
4526     {
4527         if (!w && !h) {
4528             return;
4529         }
4530         
4531         this.resizeTo(w,h);
4532     },
4533
4534     show : function() {
4535
4536         if (!this.rendered) {
4537             this.render();
4538         }
4539         this.toggleHeaderInput(false);
4540         //this.el.setStyle('display', 'block');
4541         this.el.removeClass('hideing');
4542         this.el.dom.style.display='block';
4543         
4544         Roo.get(document.body).addClass('modal-open');
4545  
4546         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4547             
4548             (function(){
4549                 this.el.addClass('show');
4550                 this.el.addClass('in');
4551             }).defer(50, this);
4552         }else{
4553             this.el.addClass('show');
4554             this.el.addClass('in');
4555         }
4556
4557         // not sure how we can show data in here..
4558         //if (this.tmpl) {
4559         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4560         //}
4561
4562         Roo.get(document.body).addClass("x-body-masked");
4563         
4564         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4565         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4566         this.maskEl.dom.style.display = 'block';
4567         this.maskEl.addClass('show');
4568         
4569         
4570         this.resize();
4571         
4572         this.fireEvent('show', this);
4573
4574         // set zindex here - otherwise it appears to be ignored...
4575         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4576
4577         (function () {
4578             this.items.forEach( function(e) {
4579                 e.layout ? e.layout() : false;
4580
4581             });
4582         }).defer(100,this);
4583
4584     },
4585     hide : function()
4586     {
4587         if(this.fireEvent("beforehide", this) !== false){
4588             
4589             this.maskEl.removeClass('show');
4590             
4591             this.maskEl.dom.style.display = '';
4592             Roo.get(document.body).removeClass("x-body-masked");
4593             this.el.removeClass('in');
4594             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4595
4596             if(this.animate){ // why
4597                 this.el.addClass('hideing');
4598                 this.el.removeClass('show');
4599                 (function(){
4600                     if (!this.el.hasClass('hideing')) {
4601                         return; // it's been shown again...
4602                     }
4603                     
4604                     this.el.dom.style.display='';
4605
4606                     Roo.get(document.body).removeClass('modal-open');
4607                     this.el.removeClass('hideing');
4608                 }).defer(150,this);
4609                 
4610             }else{
4611                 this.el.removeClass('show');
4612                 this.el.dom.style.display='';
4613                 Roo.get(document.body).removeClass('modal-open');
4614
4615             }
4616             this.fireEvent('hide', this);
4617         }
4618     },
4619     isVisible : function()
4620     {
4621         
4622         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4623         
4624     },
4625
4626     addButton : function(str, cb)
4627     {
4628
4629
4630         var b = Roo.apply({}, { html : str } );
4631         b.xns = b.xns || Roo.bootstrap;
4632         b.xtype = b.xtype || 'Button';
4633         if (typeof(b.listeners) == 'undefined') {
4634             b.listeners = { click : cb.createDelegate(this)  };
4635         }
4636
4637         var btn = Roo.factory(b);
4638
4639         btn.render(this.getButtonContainer());
4640
4641         return btn;
4642
4643     },
4644
4645     setDefaultButton : function(btn)
4646     {
4647         //this.el.select('.modal-footer').()
4648     },
4649
4650     resizeTo: function(w,h)
4651     {
4652         this.dialogEl.setWidth(w);
4653         
4654         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4655
4656         this.bodyEl.setHeight(h - diff);
4657         
4658         this.fireEvent('resize', this);
4659     },
4660     
4661     setContentSize  : function(w, h)
4662     {
4663
4664     },
4665     onButtonClick: function(btn,e)
4666     {
4667         //Roo.log([a,b,c]);
4668         this.fireEvent('btnclick', btn.name, e);
4669     },
4670      /**
4671      * Set the title of the Dialog
4672      * @param {String} str new Title
4673      */
4674     setTitle: function(str) {
4675         this.titleEl.dom.innerHTML = str;
4676         this.title = str;
4677     },
4678     /**
4679      * Set the body of the Dialog
4680      * @param {String} str new Title
4681      */
4682     setBody: function(str) {
4683         this.bodyEl.dom.innerHTML = str;
4684     },
4685     /**
4686      * Set the body of the Dialog using the template
4687      * @param {Obj} data - apply this data to the template and replace the body contents.
4688      */
4689     applyBody: function(obj)
4690     {
4691         if (!this.tmpl) {
4692             Roo.log("Error - using apply Body without a template");
4693             //code
4694         }
4695         this.tmpl.overwrite(this.bodyEl, obj);
4696     },
4697     
4698     getChildHeight : function(child_nodes)
4699     {
4700         if(
4701             !child_nodes ||
4702             child_nodes.length == 0
4703         ) {
4704             return 0;
4705         }
4706         
4707         var child_height = 0;
4708         
4709         for(var i = 0; i < child_nodes.length; i++) {
4710             
4711             /*
4712             * for modal with tabs...
4713             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4714                 
4715                 var layout_childs = child_nodes[i].childNodes;
4716                 
4717                 for(var j = 0; j < layout_childs.length; j++) {
4718                     
4719                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4720                         
4721                         var layout_body_childs = layout_childs[j].childNodes;
4722                         
4723                         for(var k = 0; k < layout_body_childs.length; k++) {
4724                             
4725                             if(layout_body_childs[k].classList.contains('navbar')) {
4726                                 child_height += layout_body_childs[k].offsetHeight;
4727                                 continue;
4728                             }
4729                             
4730                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4731                                 
4732                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4733                                 
4734                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4735                                     
4736                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4737                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4738                                         continue;
4739                                     }
4740                                     
4741                                 }
4742                                 
4743                             }
4744                             
4745                         }
4746                     }
4747                 }
4748                 continue;
4749             }
4750             */
4751             
4752             child_height += child_nodes[i].offsetHeight;
4753             // Roo.log(child_nodes[i].offsetHeight);
4754         }
4755         
4756         return child_height;
4757     },
4758     toggleHeaderInput : function(is_edit)
4759     {
4760         if (!this.editableTitle) {
4761             return; // not editable.
4762         }
4763         if (is_edit && this.is_header_editing) {
4764             return; // already editing..
4765         }
4766         if (is_edit) {
4767     
4768             this.headerEditEl.dom.value = this.title;
4769             this.headerEditEl.removeClass('d-none');
4770             this.headerEditEl.dom.focus();
4771             this.titleEl.addClass('d-none');
4772             
4773             this.is_header_editing = true;
4774             return
4775         }
4776         // flip back to not editing.
4777         this.title = this.headerEditEl.dom.value;
4778         this.headerEditEl.addClass('d-none');
4779         this.titleEl.removeClass('d-none');
4780         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4781         this.is_header_editing = false;
4782         this.fireEvent('titlechanged', this, this.title);
4783     
4784             
4785         
4786     }
4787
4788 });
4789
4790
4791 Roo.apply(Roo.bootstrap.Modal,  {
4792     /**
4793          * Button config that displays a single OK button
4794          * @type Object
4795          */
4796         OK :  [{
4797             name : 'ok',
4798             weight : 'primary',
4799             html : 'OK'
4800         }],
4801         /**
4802          * Button config that displays Yes and No buttons
4803          * @type Object
4804          */
4805         YESNO : [
4806             {
4807                 name  : 'no',
4808                 html : 'No'
4809             },
4810             {
4811                 name  :'yes',
4812                 weight : 'primary',
4813                 html : 'Yes'
4814             }
4815         ],
4816
4817         /**
4818          * Button config that displays OK and Cancel buttons
4819          * @type Object
4820          */
4821         OKCANCEL : [
4822             {
4823                name : 'cancel',
4824                 html : 'Cancel'
4825             },
4826             {
4827                 name : 'ok',
4828                 weight : 'primary',
4829                 html : 'OK'
4830             }
4831         ],
4832         /**
4833          * Button config that displays Yes, No and Cancel buttons
4834          * @type Object
4835          */
4836         YESNOCANCEL : [
4837             {
4838                 name : 'yes',
4839                 weight : 'primary',
4840                 html : 'Yes'
4841             },
4842             {
4843                 name : 'no',
4844                 html : 'No'
4845             },
4846             {
4847                 name : 'cancel',
4848                 html : 'Cancel'
4849             }
4850         ],
4851         
4852         zIndex : 10001
4853 });
4854
4855 /*
4856  * - LGPL
4857  *
4858  * messagebox - can be used as a replace
4859  * 
4860  */
4861 /**
4862  * @class Roo.MessageBox
4863  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4864  * Example usage:
4865  *<pre><code>
4866 // Basic alert:
4867 Roo.Msg.alert('Status', 'Changes saved successfully.');
4868
4869 // Prompt for user data:
4870 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4871     if (btn == 'ok'){
4872         // process text value...
4873     }
4874 });
4875
4876 // Show a dialog using config options:
4877 Roo.Msg.show({
4878    title:'Save Changes?',
4879    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4880    buttons: Roo.Msg.YESNOCANCEL,
4881    fn: processResult,
4882    animEl: 'elId'
4883 });
4884 </code></pre>
4885  * @singleton
4886  */
4887 Roo.bootstrap.MessageBox = function(){
4888     var dlg, opt, mask, waitTimer;
4889     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4890     var buttons, activeTextEl, bwidth;
4891
4892     
4893     // private
4894     var handleButton = function(button){
4895         dlg.hide();
4896         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4897     };
4898
4899     // private
4900     var handleHide = function(){
4901         if(opt && opt.cls){
4902             dlg.el.removeClass(opt.cls);
4903         }
4904         //if(waitTimer){
4905         //    Roo.TaskMgr.stop(waitTimer);
4906         //    waitTimer = null;
4907         //}
4908     };
4909
4910     // private
4911     var updateButtons = function(b){
4912         var width = 0;
4913         if(!b){
4914             buttons["ok"].hide();
4915             buttons["cancel"].hide();
4916             buttons["yes"].hide();
4917             buttons["no"].hide();
4918             dlg.footerEl.hide();
4919             
4920             return width;
4921         }
4922         dlg.footerEl.show();
4923         for(var k in buttons){
4924             if(typeof buttons[k] != "function"){
4925                 if(b[k]){
4926                     buttons[k].show();
4927                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4928                     width += buttons[k].el.getWidth()+15;
4929                 }else{
4930                     buttons[k].hide();
4931                 }
4932             }
4933         }
4934         return width;
4935     };
4936
4937     // private
4938     var handleEsc = function(d, k, e){
4939         if(opt && opt.closable !== false){
4940             dlg.hide();
4941         }
4942         if(e){
4943             e.stopEvent();
4944         }
4945     };
4946
4947     return {
4948         /**
4949          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4950          * @return {Roo.BasicDialog} The BasicDialog element
4951          */
4952         getDialog : function(){
4953            if(!dlg){
4954                 dlg = new Roo.bootstrap.Modal( {
4955                     //draggable: true,
4956                     //resizable:false,
4957                     //constraintoviewport:false,
4958                     //fixedcenter:true,
4959                     //collapsible : false,
4960                     //shim:true,
4961                     //modal: true,
4962                 //    width: 'auto',
4963                   //  height:100,
4964                     //buttonAlign:"center",
4965                     closeClick : function(){
4966                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4967                             handleButton("no");
4968                         }else{
4969                             handleButton("cancel");
4970                         }
4971                     }
4972                 });
4973                 dlg.render();
4974                 dlg.on("hide", handleHide);
4975                 mask = dlg.mask;
4976                 //dlg.addKeyListener(27, handleEsc);
4977                 buttons = {};
4978                 this.buttons = buttons;
4979                 var bt = this.buttonText;
4980                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4981                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4982                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4983                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4984                 //Roo.log(buttons);
4985                 bodyEl = dlg.bodyEl.createChild({
4986
4987                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4988                         '<textarea class="roo-mb-textarea"></textarea>' +
4989                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4990                 });
4991                 msgEl = bodyEl.dom.firstChild;
4992                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4993                 textboxEl.enableDisplayMode();
4994                 textboxEl.addKeyListener([10,13], function(){
4995                     if(dlg.isVisible() && opt && opt.buttons){
4996                         if(opt.buttons.ok){
4997                             handleButton("ok");
4998                         }else if(opt.buttons.yes){
4999                             handleButton("yes");
5000                         }
5001                     }
5002                 });
5003                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5004                 textareaEl.enableDisplayMode();
5005                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5006                 progressEl.enableDisplayMode();
5007                 
5008                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5009                 var pf = progressEl.dom.firstChild;
5010                 if (pf) {
5011                     pp = Roo.get(pf.firstChild);
5012                     pp.setHeight(pf.offsetHeight);
5013                 }
5014                 
5015             }
5016             return dlg;
5017         },
5018
5019         /**
5020          * Updates the message box body text
5021          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5022          * the XHTML-compliant non-breaking space character '&amp;#160;')
5023          * @return {Roo.MessageBox} This message box
5024          */
5025         updateText : function(text)
5026         {
5027             if(!dlg.isVisible() && !opt.width){
5028                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5029                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5030             }
5031             msgEl.innerHTML = text || '&#160;';
5032       
5033             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5034             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5035             var w = Math.max(
5036                     Math.min(opt.width || cw , this.maxWidth), 
5037                     Math.max(opt.minWidth || this.minWidth, bwidth)
5038             );
5039             if(opt.prompt){
5040                 activeTextEl.setWidth(w);
5041             }
5042             if(dlg.isVisible()){
5043                 dlg.fixedcenter = false;
5044             }
5045             // to big, make it scroll. = But as usual stupid IE does not support
5046             // !important..
5047             
5048             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5049                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5050                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5051             } else {
5052                 bodyEl.dom.style.height = '';
5053                 bodyEl.dom.style.overflowY = '';
5054             }
5055             if (cw > w) {
5056                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5057             } else {
5058                 bodyEl.dom.style.overflowX = '';
5059             }
5060             
5061             dlg.setContentSize(w, bodyEl.getHeight());
5062             if(dlg.isVisible()){
5063                 dlg.fixedcenter = true;
5064             }
5065             return this;
5066         },
5067
5068         /**
5069          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5070          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5071          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5072          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5073          * @return {Roo.MessageBox} This message box
5074          */
5075         updateProgress : function(value, text){
5076             if(text){
5077                 this.updateText(text);
5078             }
5079             
5080             if (pp) { // weird bug on my firefox - for some reason this is not defined
5081                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5082                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5083             }
5084             return this;
5085         },        
5086
5087         /**
5088          * Returns true if the message box is currently displayed
5089          * @return {Boolean} True if the message box is visible, else false
5090          */
5091         isVisible : function(){
5092             return dlg && dlg.isVisible();  
5093         },
5094
5095         /**
5096          * Hides the message box if it is displayed
5097          */
5098         hide : function(){
5099             if(this.isVisible()){
5100                 dlg.hide();
5101             }  
5102         },
5103
5104         /**
5105          * Displays a new message box, or reinitializes an existing message box, based on the config options
5106          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5107          * The following config object properties are supported:
5108          * <pre>
5109 Property    Type             Description
5110 ----------  ---------------  ------------------------------------------------------------------------------------
5111 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5112                                    closes (defaults to undefined)
5113 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5114                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5115 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5116                                    progress and wait dialogs will ignore this property and always hide the
5117                                    close button as they can only be closed programmatically.
5118 cls               String           A custom CSS class to apply to the message box element
5119 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5120                                    displayed (defaults to 75)
5121 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5122                                    function will be btn (the name of the button that was clicked, if applicable,
5123                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5124                                    Progress and wait dialogs will ignore this option since they do not respond to
5125                                    user actions and can only be closed programmatically, so any required function
5126                                    should be called by the same code after it closes the dialog.
5127 icon              String           A CSS class that provides a background image to be used as an icon for
5128                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5129 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5130 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5131 modal             Boolean          False to allow user interaction with the page while the message box is
5132                                    displayed (defaults to true)
5133 msg               String           A string that will replace the existing message box body text (defaults
5134                                    to the XHTML-compliant non-breaking space character '&#160;')
5135 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5136 progress          Boolean          True to display a progress bar (defaults to false)
5137 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5138 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5139 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5140 title             String           The title text
5141 value             String           The string value to set into the active textbox element if displayed
5142 wait              Boolean          True to display a progress bar (defaults to false)
5143 width             Number           The width of the dialog in pixels
5144 </pre>
5145          *
5146          * Example usage:
5147          * <pre><code>
5148 Roo.Msg.show({
5149    title: 'Address',
5150    msg: 'Please enter your address:',
5151    width: 300,
5152    buttons: Roo.MessageBox.OKCANCEL,
5153    multiline: true,
5154    fn: saveAddress,
5155    animEl: 'addAddressBtn'
5156 });
5157 </code></pre>
5158          * @param {Object} config Configuration options
5159          * @return {Roo.MessageBox} This message box
5160          */
5161         show : function(options)
5162         {
5163             
5164             // this causes nightmares if you show one dialog after another
5165             // especially on callbacks..
5166              
5167             if(this.isVisible()){
5168                 
5169                 this.hide();
5170                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5171                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5172                 Roo.log("New Dialog Message:" +  options.msg )
5173                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5174                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5175                 
5176             }
5177             var d = this.getDialog();
5178             opt = options;
5179             d.setTitle(opt.title || "&#160;");
5180             d.closeEl.setDisplayed(opt.closable !== false);
5181             activeTextEl = textboxEl;
5182             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5183             if(opt.prompt){
5184                 if(opt.multiline){
5185                     textboxEl.hide();
5186                     textareaEl.show();
5187                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5188                         opt.multiline : this.defaultTextHeight);
5189                     activeTextEl = textareaEl;
5190                 }else{
5191                     textboxEl.show();
5192                     textareaEl.hide();
5193                 }
5194             }else{
5195                 textboxEl.hide();
5196                 textareaEl.hide();
5197             }
5198             progressEl.setDisplayed(opt.progress === true);
5199             if (opt.progress) {
5200                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5201             }
5202             this.updateProgress(0);
5203             activeTextEl.dom.value = opt.value || "";
5204             if(opt.prompt){
5205                 dlg.setDefaultButton(activeTextEl);
5206             }else{
5207                 var bs = opt.buttons;
5208                 var db = null;
5209                 if(bs && bs.ok){
5210                     db = buttons["ok"];
5211                 }else if(bs && bs.yes){
5212                     db = buttons["yes"];
5213                 }
5214                 dlg.setDefaultButton(db);
5215             }
5216             bwidth = updateButtons(opt.buttons);
5217             this.updateText(opt.msg);
5218             if(opt.cls){
5219                 d.el.addClass(opt.cls);
5220             }
5221             d.proxyDrag = opt.proxyDrag === true;
5222             d.modal = opt.modal !== false;
5223             d.mask = opt.modal !== false ? mask : false;
5224             if(!d.isVisible()){
5225                 // force it to the end of the z-index stack so it gets a cursor in FF
5226                 document.body.appendChild(dlg.el.dom);
5227                 d.animateTarget = null;
5228                 d.show(options.animEl);
5229             }
5230             return this;
5231         },
5232
5233         /**
5234          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5235          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5236          * and closing the message box when the process is complete.
5237          * @param {String} title The title bar text
5238          * @param {String} msg The message box body text
5239          * @return {Roo.MessageBox} This message box
5240          */
5241         progress : function(title, msg){
5242             this.show({
5243                 title : title,
5244                 msg : msg,
5245                 buttons: false,
5246                 progress:true,
5247                 closable:false,
5248                 minWidth: this.minProgressWidth,
5249                 modal : true
5250             });
5251             return this;
5252         },
5253
5254         /**
5255          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5256          * If a callback function is passed it will be called after the user clicks the button, and the
5257          * id of the button that was clicked will be passed as the only parameter to the callback
5258          * (could also be the top-right close button).
5259          * @param {String} title The title bar text
5260          * @param {String} msg The message box body text
5261          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5262          * @param {Object} scope (optional) The scope of the callback function
5263          * @return {Roo.MessageBox} This message box
5264          */
5265         alert : function(title, msg, fn, scope)
5266         {
5267             this.show({
5268                 title : title,
5269                 msg : msg,
5270                 buttons: this.OK,
5271                 fn: fn,
5272                 closable : false,
5273                 scope : scope,
5274                 modal : true
5275             });
5276             return this;
5277         },
5278
5279         /**
5280          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5281          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5282          * You are responsible for closing the message box when the process is complete.
5283          * @param {String} msg The message box body text
5284          * @param {String} title (optional) The title bar text
5285          * @return {Roo.MessageBox} This message box
5286          */
5287         wait : function(msg, title){
5288             this.show({
5289                 title : title,
5290                 msg : msg,
5291                 buttons: false,
5292                 closable:false,
5293                 progress:true,
5294                 modal:true,
5295                 width:300,
5296                 wait:true
5297             });
5298             waitTimer = Roo.TaskMgr.start({
5299                 run: function(i){
5300                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5301                 },
5302                 interval: 1000
5303             });
5304             return this;
5305         },
5306
5307         /**
5308          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5309          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5310          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5311          * @param {String} title The title bar text
5312          * @param {String} msg The message box body text
5313          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5314          * @param {Object} scope (optional) The scope of the callback function
5315          * @return {Roo.MessageBox} This message box
5316          */
5317         confirm : function(title, msg, fn, scope){
5318             this.show({
5319                 title : title,
5320                 msg : msg,
5321                 buttons: this.YESNO,
5322                 fn: fn,
5323                 scope : scope,
5324                 modal : true
5325             });
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5331          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5332          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5333          * (could also be the top-right close button) and the text that was entered will be passed as the two
5334          * parameters to the callback.
5335          * @param {String} title The title bar text
5336          * @param {String} msg The message box body text
5337          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5338          * @param {Object} scope (optional) The scope of the callback function
5339          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5340          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         prompt : function(title, msg, fn, scope, multiline){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: this.OKCANCEL,
5348                 fn: fn,
5349                 minWidth:250,
5350                 scope : scope,
5351                 prompt:true,
5352                 multiline: multiline,
5353                 modal : true
5354             });
5355             return this;
5356         },
5357
5358         /**
5359          * Button config that displays a single OK button
5360          * @type Object
5361          */
5362         OK : {ok:true},
5363         /**
5364          * Button config that displays Yes and No buttons
5365          * @type Object
5366          */
5367         YESNO : {yes:true, no:true},
5368         /**
5369          * Button config that displays OK and Cancel buttons
5370          * @type Object
5371          */
5372         OKCANCEL : {ok:true, cancel:true},
5373         /**
5374          * Button config that displays Yes, No and Cancel buttons
5375          * @type Object
5376          */
5377         YESNOCANCEL : {yes:true, no:true, cancel:true},
5378
5379         /**
5380          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5381          * @type Number
5382          */
5383         defaultTextHeight : 75,
5384         /**
5385          * The maximum width in pixels of the message box (defaults to 600)
5386          * @type Number
5387          */
5388         maxWidth : 600,
5389         /**
5390          * The minimum width in pixels of the message box (defaults to 100)
5391          * @type Number
5392          */
5393         minWidth : 100,
5394         /**
5395          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5396          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5397          * @type Number
5398          */
5399         minProgressWidth : 250,
5400         /**
5401          * An object containing the default button text strings that can be overriden for localized language support.
5402          * Supported properties are: ok, cancel, yes and no.
5403          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5404          * @type Object
5405          */
5406         buttonText : {
5407             ok : "OK",
5408             cancel : "Cancel",
5409             yes : "Yes",
5410             no : "No"
5411         }
5412     };
5413 }();
5414
5415 /**
5416  * Shorthand for {@link Roo.MessageBox}
5417  */
5418 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5419 Roo.Msg = Roo.Msg || Roo.MessageBox;
5420 /*
5421  * - LGPL
5422  *
5423  * navbar
5424  * 
5425  */
5426
5427 /**
5428  * @class Roo.bootstrap.Navbar
5429  * @extends Roo.bootstrap.Component
5430  * Bootstrap Navbar class
5431
5432  * @constructor
5433  * Create a new Navbar
5434  * @param {Object} config The config object
5435  */
5436
5437
5438 Roo.bootstrap.Navbar = function(config){
5439     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5440     this.addEvents({
5441         // raw events
5442         /**
5443          * @event beforetoggle
5444          * Fire before toggle the menu
5445          * @param {Roo.EventObject} e
5446          */
5447         "beforetoggle" : true
5448     });
5449 };
5450
5451 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5452     
5453     
5454    
5455     // private
5456     navItems : false,
5457     loadMask : false,
5458     
5459     
5460     getAutoCreate : function(){
5461         
5462         
5463         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5464         
5465     },
5466     
5467     initEvents :function ()
5468     {
5469         //Roo.log(this.el.select('.navbar-toggle',true));
5470         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5471         
5472         var mark = {
5473             tag: "div",
5474             cls:"x-dlg-mask"
5475         };
5476         
5477         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5478         
5479         var size = this.el.getSize();
5480         this.maskEl.setSize(size.width, size.height);
5481         this.maskEl.enableDisplayMode("block");
5482         this.maskEl.hide();
5483         
5484         if(this.loadMask){
5485             this.maskEl.show();
5486         }
5487     },
5488     
5489     
5490     getChildContainer : function()
5491     {
5492         if (this.el && this.el.select('.collapse').getCount()) {
5493             return this.el.select('.collapse',true).first();
5494         }
5495         
5496         return this.el;
5497     },
5498     
5499     mask : function()
5500     {
5501         this.maskEl.show();
5502     },
5503     
5504     unmask : function()
5505     {
5506         this.maskEl.hide();
5507     },
5508     onToggle : function()
5509     {
5510         
5511         if(this.fireEvent('beforetoggle', this) === false){
5512             return;
5513         }
5514         var ce = this.el.select('.navbar-collapse',true).first();
5515       
5516         if (!ce.hasClass('show')) {
5517            this.expand();
5518         } else {
5519             this.collapse();
5520         }
5521         
5522         
5523     
5524     },
5525     /**
5526      * Expand the navbar pulldown 
5527      */
5528     expand : function ()
5529     {
5530        
5531         var ce = this.el.select('.navbar-collapse',true).first();
5532         if (ce.hasClass('collapsing')) {
5533             return;
5534         }
5535         ce.dom.style.height = '';
5536                // show it...
5537         ce.addClass('in'); // old...
5538         ce.removeClass('collapse');
5539         ce.addClass('show');
5540         var h = ce.getHeight();
5541         Roo.log(h);
5542         ce.removeClass('show');
5543         // at this point we should be able to see it..
5544         ce.addClass('collapsing');
5545         
5546         ce.setHeight(0); // resize it ...
5547         ce.on('transitionend', function() {
5548             //Roo.log('done transition');
5549             ce.removeClass('collapsing');
5550             ce.addClass('show');
5551             ce.removeClass('collapse');
5552
5553             ce.dom.style.height = '';
5554         }, this, { single: true} );
5555         ce.setHeight(h);
5556         ce.dom.scrollTop = 0;
5557     },
5558     /**
5559      * Collapse the navbar pulldown 
5560      */
5561     collapse : function()
5562     {
5563          var ce = this.el.select('.navbar-collapse',true).first();
5564        
5565         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5566             // it's collapsed or collapsing..
5567             return;
5568         }
5569         ce.removeClass('in'); // old...
5570         ce.setHeight(ce.getHeight());
5571         ce.removeClass('show');
5572         ce.addClass('collapsing');
5573         
5574         ce.on('transitionend', function() {
5575             ce.dom.style.height = '';
5576             ce.removeClass('collapsing');
5577             ce.addClass('collapse');
5578         }, this, { single: true} );
5579         ce.setHeight(0);
5580     }
5581     
5582     
5583     
5584 });
5585
5586
5587
5588  
5589
5590  /*
5591  * - LGPL
5592  *
5593  * navbar
5594  * 
5595  */
5596
5597 /**
5598  * @class Roo.bootstrap.NavSimplebar
5599  * @extends Roo.bootstrap.Navbar
5600  * Bootstrap Sidebar class
5601  *
5602  * @cfg {Boolean} inverse is inverted color
5603  * 
5604  * @cfg {String} type (nav | pills | tabs)
5605  * @cfg {Boolean} arrangement stacked | justified
5606  * @cfg {String} align (left | right) alignment
5607  * 
5608  * @cfg {Boolean} main (true|false) main nav bar? default false
5609  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5610  * 
5611  * @cfg {String} tag (header|footer|nav|div) default is nav 
5612
5613  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5614  * 
5615  * 
5616  * @constructor
5617  * Create a new Sidebar
5618  * @param {Object} config The config object
5619  */
5620
5621
5622 Roo.bootstrap.NavSimplebar = function(config){
5623     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5624 };
5625
5626 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5627     
5628     inverse: false,
5629     
5630     type: false,
5631     arrangement: '',
5632     align : false,
5633     
5634     weight : 'light',
5635     
5636     main : false,
5637     
5638     
5639     tag : false,
5640     
5641     
5642     getAutoCreate : function(){
5643         
5644         
5645         var cfg = {
5646             tag : this.tag || 'div',
5647             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5648         };
5649         if (['light','white'].indexOf(this.weight) > -1) {
5650             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5651         }
5652         cfg.cls += ' bg-' + this.weight;
5653         
5654         if (this.inverse) {
5655             cfg.cls += ' navbar-inverse';
5656             
5657         }
5658         
5659         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5660         
5661         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5662             return cfg;
5663         }
5664         
5665         
5666     
5667         
5668         cfg.cn = [
5669             {
5670                 cls: 'nav nav-' + this.xtype,
5671                 tag : 'ul'
5672             }
5673         ];
5674         
5675          
5676         this.type = this.type || 'nav';
5677         if (['tabs','pills'].indexOf(this.type) != -1) {
5678             cfg.cn[0].cls += ' nav-' + this.type
5679         
5680         
5681         } else {
5682             if (this.type!=='nav') {
5683                 Roo.log('nav type must be nav/tabs/pills')
5684             }
5685             cfg.cn[0].cls += ' navbar-nav'
5686         }
5687         
5688         
5689         
5690         
5691         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5692             cfg.cn[0].cls += ' nav-' + this.arrangement;
5693         }
5694         
5695         
5696         if (this.align === 'right') {
5697             cfg.cn[0].cls += ' navbar-right';
5698         }
5699         
5700         
5701         
5702         
5703         return cfg;
5704     
5705         
5706     }
5707     
5708     
5709     
5710 });
5711
5712
5713
5714  
5715
5716  
5717        /*
5718  * - LGPL
5719  *
5720  * navbar
5721  * navbar-fixed-top
5722  * navbar-expand-md  fixed-top 
5723  */
5724
5725 /**
5726  * @class Roo.bootstrap.NavHeaderbar
5727  * @extends Roo.bootstrap.NavSimplebar
5728  * Bootstrap Sidebar class
5729  *
5730  * @cfg {String} brand what is brand
5731  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5732  * @cfg {String} brand_href href of the brand
5733  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5734  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5735  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5736  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5737  * 
5738  * @constructor
5739  * Create a new Sidebar
5740  * @param {Object} config The config object
5741  */
5742
5743
5744 Roo.bootstrap.NavHeaderbar = function(config){
5745     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5746       
5747 };
5748
5749 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5750     
5751     position: '',
5752     brand: '',
5753     brand_href: false,
5754     srButton : true,
5755     autohide : false,
5756     desktopCenter : false,
5757    
5758     
5759     getAutoCreate : function(){
5760         
5761         var   cfg = {
5762             tag: this.nav || 'nav',
5763             cls: 'navbar navbar-expand-md',
5764             role: 'navigation',
5765             cn: []
5766         };
5767         
5768         var cn = cfg.cn;
5769         if (this.desktopCenter) {
5770             cn.push({cls : 'container', cn : []});
5771             cn = cn[0].cn;
5772         }
5773         
5774         if(this.srButton){
5775             var btn = {
5776                 tag: 'button',
5777                 type: 'button',
5778                 cls: 'navbar-toggle navbar-toggler',
5779                 'data-toggle': 'collapse',
5780                 cn: [
5781                     {
5782                         tag: 'span',
5783                         cls: 'sr-only',
5784                         html: 'Toggle navigation'
5785                     },
5786                     {
5787                         tag: 'span',
5788                         cls: 'icon-bar navbar-toggler-icon'
5789                     },
5790                     {
5791                         tag: 'span',
5792                         cls: 'icon-bar'
5793                     },
5794                     {
5795                         tag: 'span',
5796                         cls: 'icon-bar'
5797                     }
5798                 ]
5799             };
5800             
5801             cn.push( Roo.bootstrap.version == 4 ? btn : {
5802                 tag: 'div',
5803                 cls: 'navbar-header',
5804                 cn: [
5805                     btn
5806                 ]
5807             });
5808         }
5809         
5810         cn.push({
5811             tag: 'div',
5812             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5813             cn : []
5814         });
5815         
5816         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5817         
5818         if (['light','white'].indexOf(this.weight) > -1) {
5819             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5820         }
5821         cfg.cls += ' bg-' + this.weight;
5822         
5823         
5824         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5825             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5826             
5827             // tag can override this..
5828             
5829             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5830         }
5831         
5832         if (this.brand !== '') {
5833             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5834             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5835                 tag: 'a',
5836                 href: this.brand_href ? this.brand_href : '#',
5837                 cls: 'navbar-brand',
5838                 cn: [
5839                 this.brand
5840                 ]
5841             });
5842         }
5843         
5844         if(this.main){
5845             cfg.cls += ' main-nav';
5846         }
5847         
5848         
5849         return cfg;
5850
5851         
5852     },
5853     getHeaderChildContainer : function()
5854     {
5855         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5856             return this.el.select('.navbar-header',true).first();
5857         }
5858         
5859         return this.getChildContainer();
5860     },
5861     
5862     getChildContainer : function()
5863     {
5864          
5865         return this.el.select('.roo-navbar-collapse',true).first();
5866          
5867         
5868     },
5869     
5870     initEvents : function()
5871     {
5872         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5873         
5874         if (this.autohide) {
5875             
5876             var prevScroll = 0;
5877             var ft = this.el;
5878             
5879             Roo.get(document).on('scroll',function(e) {
5880                 var ns = Roo.get(document).getScroll().top;
5881                 var os = prevScroll;
5882                 prevScroll = ns;
5883                 
5884                 if(ns > os){
5885                     ft.removeClass('slideDown');
5886                     ft.addClass('slideUp');
5887                     return;
5888                 }
5889                 ft.removeClass('slideUp');
5890                 ft.addClass('slideDown');
5891                  
5892               
5893           },this);
5894         }
5895     }    
5896     
5897 });
5898
5899
5900
5901  
5902
5903  /*
5904  * - LGPL
5905  *
5906  * navbar
5907  * 
5908  */
5909
5910 /**
5911  * @class Roo.bootstrap.NavSidebar
5912  * @extends Roo.bootstrap.Navbar
5913  * Bootstrap Sidebar class
5914  * 
5915  * @constructor
5916  * Create a new Sidebar
5917  * @param {Object} config The config object
5918  */
5919
5920
5921 Roo.bootstrap.NavSidebar = function(config){
5922     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5923 };
5924
5925 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5926     
5927     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5928     
5929     getAutoCreate : function(){
5930         
5931         
5932         return  {
5933             tag: 'div',
5934             cls: 'sidebar sidebar-nav'
5935         };
5936     
5937         
5938     }
5939     
5940     
5941     
5942 });
5943
5944
5945
5946  
5947
5948  /*
5949  * - LGPL
5950  *
5951  * nav group
5952  * 
5953  */
5954
5955 /**
5956  * @class Roo.bootstrap.NavGroup
5957  * @extends Roo.bootstrap.Component
5958  * Bootstrap NavGroup class
5959  * @cfg {String} align (left|right)
5960  * @cfg {Boolean} inverse
5961  * @cfg {String} type (nav|pills|tab) default nav
5962  * @cfg {String} navId - reference Id for navbar.
5963  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5964  * 
5965  * @constructor
5966  * Create a new nav group
5967  * @param {Object} config The config object
5968  */
5969
5970 Roo.bootstrap.NavGroup = function(config){
5971     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5972     this.navItems = [];
5973    
5974     Roo.bootstrap.NavGroup.register(this);
5975      this.addEvents({
5976         /**
5977              * @event changed
5978              * Fires when the active item changes
5979              * @param {Roo.bootstrap.NavGroup} this
5980              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5981              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5982          */
5983         'changed': true
5984      });
5985     
5986 };
5987
5988 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5989     
5990     align: '',
5991     inverse: false,
5992     form: false,
5993     type: 'nav',
5994     navId : '',
5995     // private
5996     pilltype : true,
5997     
5998     navItems : false, 
5999     
6000     getAutoCreate : function()
6001     {
6002         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6003         
6004         cfg = {
6005             tag : 'ul',
6006             cls: 'nav' 
6007         };
6008         if (Roo.bootstrap.version == 4) {
6009             if (['tabs','pills'].indexOf(this.type) != -1) {
6010                 cfg.cls += ' nav-' + this.type; 
6011             } else {
6012                 // trying to remove so header bar can right align top?
6013                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6014                     // do not use on header bar... 
6015                     cfg.cls += ' navbar-nav';
6016                 }
6017             }
6018             
6019         } else {
6020             if (['tabs','pills'].indexOf(this.type) != -1) {
6021                 cfg.cls += ' nav-' + this.type
6022             } else {
6023                 if (this.type !== 'nav') {
6024                     Roo.log('nav type must be nav/tabs/pills')
6025                 }
6026                 cfg.cls += ' navbar-nav'
6027             }
6028         }
6029         
6030         if (this.parent() && this.parent().sidebar) {
6031             cfg = {
6032                 tag: 'ul',
6033                 cls: 'dashboard-menu sidebar-menu'
6034             };
6035             
6036             return cfg;
6037         }
6038         
6039         if (this.form === true) {
6040             cfg = {
6041                 tag: 'form',
6042                 cls: 'navbar-form form-inline'
6043             };
6044             //nav navbar-right ml-md-auto
6045             if (this.align === 'right') {
6046                 cfg.cls += ' navbar-right ml-md-auto';
6047             } else {
6048                 cfg.cls += ' navbar-left';
6049             }
6050         }
6051         
6052         if (this.align === 'right') {
6053             cfg.cls += ' navbar-right ml-md-auto';
6054         } else {
6055             cfg.cls += ' mr-auto';
6056         }
6057         
6058         if (this.inverse) {
6059             cfg.cls += ' navbar-inverse';
6060             
6061         }
6062         
6063         
6064         return cfg;
6065     },
6066     /**
6067     * sets the active Navigation item
6068     * @param {Roo.bootstrap.NavItem} the new current navitem
6069     */
6070     setActiveItem : function(item)
6071     {
6072         var prev = false;
6073         Roo.each(this.navItems, function(v){
6074             if (v == item) {
6075                 return ;
6076             }
6077             if (v.isActive()) {
6078                 v.setActive(false, true);
6079                 prev = v;
6080                 
6081             }
6082             
6083         });
6084
6085         item.setActive(true, true);
6086         this.fireEvent('changed', this, item, prev);
6087         
6088         
6089     },
6090     /**
6091     * gets the active Navigation item
6092     * @return {Roo.bootstrap.NavItem} the current navitem
6093     */
6094     getActive : function()
6095     {
6096         
6097         var prev = false;
6098         Roo.each(this.navItems, function(v){
6099             
6100             if (v.isActive()) {
6101                 prev = v;
6102                 
6103             }
6104             
6105         });
6106         return prev;
6107     },
6108     
6109     indexOfNav : function()
6110     {
6111         
6112         var prev = false;
6113         Roo.each(this.navItems, function(v,i){
6114             
6115             if (v.isActive()) {
6116                 prev = i;
6117                 
6118             }
6119             
6120         });
6121         return prev;
6122     },
6123     /**
6124     * adds a Navigation item
6125     * @param {Roo.bootstrap.NavItem} the navitem to add
6126     */
6127     addItem : function(cfg)
6128     {
6129         if (this.form && Roo.bootstrap.version == 4) {
6130             cfg.tag = 'div';
6131         }
6132         var cn = new Roo.bootstrap.NavItem(cfg);
6133         this.register(cn);
6134         cn.parentId = this.id;
6135         cn.onRender(this.el, null);
6136         return cn;
6137     },
6138     /**
6139     * register a Navigation item
6140     * @param {Roo.bootstrap.NavItem} the navitem to add
6141     */
6142     register : function(item)
6143     {
6144         this.navItems.push( item);
6145         item.navId = this.navId;
6146     
6147     },
6148     
6149     /**
6150     * clear all the Navigation item
6151     */
6152    
6153     clearAll : function()
6154     {
6155         this.navItems = [];
6156         this.el.dom.innerHTML = '';
6157     },
6158     
6159     getNavItem: function(tabId)
6160     {
6161         var ret = false;
6162         Roo.each(this.navItems, function(e) {
6163             if (e.tabId == tabId) {
6164                ret =  e;
6165                return false;
6166             }
6167             return true;
6168             
6169         });
6170         return ret;
6171     },
6172     
6173     setActiveNext : function()
6174     {
6175         var i = this.indexOfNav(this.getActive());
6176         if (i > this.navItems.length) {
6177             return;
6178         }
6179         this.setActiveItem(this.navItems[i+1]);
6180     },
6181     setActivePrev : function()
6182     {
6183         var i = this.indexOfNav(this.getActive());
6184         if (i  < 1) {
6185             return;
6186         }
6187         this.setActiveItem(this.navItems[i-1]);
6188     },
6189     clearWasActive : function(except) {
6190         Roo.each(this.navItems, function(e) {
6191             if (e.tabId != except.tabId && e.was_active) {
6192                e.was_active = false;
6193                return false;
6194             }
6195             return true;
6196             
6197         });
6198     },
6199     getWasActive : function ()
6200     {
6201         var r = false;
6202         Roo.each(this.navItems, function(e) {
6203             if (e.was_active) {
6204                r = e;
6205                return false;
6206             }
6207             return true;
6208             
6209         });
6210         return r;
6211     }
6212     
6213     
6214 });
6215
6216  
6217 Roo.apply(Roo.bootstrap.NavGroup, {
6218     
6219     groups: {},
6220      /**
6221     * register a Navigation Group
6222     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6223     */
6224     register : function(navgrp)
6225     {
6226         this.groups[navgrp.navId] = navgrp;
6227         
6228     },
6229     /**
6230     * fetch a Navigation Group based on the navigation ID
6231     * @param {string} the navgroup to add
6232     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6233     */
6234     get: function(navId) {
6235         if (typeof(this.groups[navId]) == 'undefined') {
6236             return false;
6237             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6238         }
6239         return this.groups[navId] ;
6240     }
6241     
6242     
6243     
6244 });
6245
6246  /*
6247  * - LGPL
6248  *
6249  * row
6250  * 
6251  */
6252
6253 /**
6254  * @class Roo.bootstrap.NavItem
6255  * @extends Roo.bootstrap.Component
6256  * Bootstrap Navbar.NavItem class
6257  * @cfg {String} href  link to
6258  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6259  * @cfg {Boolean} button_outline show and outlined button
6260  * @cfg {String} html content of button
6261  * @cfg {String} badge text inside badge
6262  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6263  * @cfg {String} glyphicon DEPRICATED - use fa
6264  * @cfg {String} icon DEPRICATED - use fa
6265  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6266  * @cfg {Boolean} active Is item active
6267  * @cfg {Boolean} disabled Is item disabled
6268  * @cfg {String} linkcls  Link Class
6269  * @cfg {Boolean} preventDefault (true | false) default false
6270  * @cfg {String} tabId the tab that this item activates.
6271  * @cfg {String} tagtype (a|span) render as a href or span?
6272  * @cfg {Boolean} animateRef (true|false) link to element default false  
6273   
6274  * @constructor
6275  * Create a new Navbar Item
6276  * @param {Object} config The config object
6277  */
6278 Roo.bootstrap.NavItem = function(config){
6279     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6280     this.addEvents({
6281         // raw events
6282         /**
6283          * @event click
6284          * The raw click event for the entire grid.
6285          * @param {Roo.EventObject} e
6286          */
6287         "click" : true,
6288          /**
6289             * @event changed
6290             * Fires when the active item active state changes
6291             * @param {Roo.bootstrap.NavItem} this
6292             * @param {boolean} state the new state
6293              
6294          */
6295         'changed': true,
6296         /**
6297             * @event scrollto
6298             * Fires when scroll to element
6299             * @param {Roo.bootstrap.NavItem} this
6300             * @param {Object} options
6301             * @param {Roo.EventObject} e
6302              
6303          */
6304         'scrollto': true
6305     });
6306    
6307 };
6308
6309 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6310     
6311     href: false,
6312     html: '',
6313     badge: '',
6314     icon: false,
6315     fa : false,
6316     glyphicon: false,
6317     active: false,
6318     preventDefault : false,
6319     tabId : false,
6320     tagtype : 'a',
6321     tag: 'li',
6322     disabled : false,
6323     animateRef : false,
6324     was_active : false,
6325     button_weight : '',
6326     button_outline : false,
6327     linkcls : '',
6328     navLink: false,
6329     
6330     getAutoCreate : function(){
6331          
6332         var cfg = {
6333             tag: this.tag,
6334             cls: 'nav-item'
6335         };
6336         
6337         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6338         
6339         if (this.active) {
6340             cfg.cls +=  ' active' ;
6341         }
6342         if (this.disabled) {
6343             cfg.cls += ' disabled';
6344         }
6345         
6346         // BS4 only?
6347         if (this.button_weight.length) {
6348             cfg.tag = this.href ? 'a' : 'button';
6349             cfg.html = this.html || '';
6350             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6351             if (this.href) {
6352                 cfg.href = this.href;
6353             }
6354             if (this.fa) {
6355                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6356             } else {
6357                 cfg.cls += " nav-html";
6358             }
6359             
6360             // menu .. should add dropdown-menu class - so no need for carat..
6361             
6362             if (this.badge !== '') {
6363                  
6364                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6365             }
6366             return cfg;
6367         }
6368         
6369         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6370             cfg.cn = [
6371                 {
6372                     tag: this.tagtype,
6373                     href : this.href || "#",
6374                     html: this.html || '',
6375                     cls : ''
6376                 }
6377             ];
6378             if (this.tagtype == 'a') {
6379                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6380         
6381             }
6382             if (this.icon) {
6383                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6384             } else  if (this.fa) {
6385                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6386             } else if(this.glyphicon) {
6387                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6388             } else {
6389                 cfg.cn[0].cls += " nav-html";
6390             }
6391             
6392             if (this.menu) {
6393                 cfg.cn[0].html += " <span class='caret'></span>";
6394              
6395             }
6396             
6397             if (this.badge !== '') {
6398                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6399             }
6400         }
6401         
6402         
6403         
6404         return cfg;
6405     },
6406     onRender : function(ct, position)
6407     {
6408        // Roo.log("Call onRender: " + this.xtype);
6409         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6410             this.tag = 'div';
6411         }
6412         
6413         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6414         this.navLink = this.el.select('.nav-link',true).first();
6415         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6416         return ret;
6417     },
6418       
6419     
6420     initEvents: function() 
6421     {
6422         if (typeof (this.menu) != 'undefined') {
6423             this.menu.parentType = this.xtype;
6424             this.menu.triggerEl = this.el;
6425             this.menu = this.addxtype(Roo.apply({}, this.menu));
6426         }
6427         
6428         this.el.on('click', this.onClick, this);
6429         
6430         //if(this.tagtype == 'span'){
6431         //    this.el.select('span',true).on('click', this.onClick, this);
6432         //}
6433        
6434         // at this point parent should be available..
6435         this.parent().register(this);
6436     },
6437     
6438     onClick : function(e)
6439     {
6440         if (e.getTarget('.dropdown-menu-item')) {
6441             // did you click on a menu itemm.... - then don't trigger onclick..
6442             return;
6443         }
6444         
6445         if(
6446                 this.preventDefault || 
6447                 this.href == '#' 
6448         ){
6449             Roo.log("NavItem - prevent Default?");
6450             e.preventDefault();
6451         }
6452         
6453         if (this.disabled) {
6454             return;
6455         }
6456         
6457         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6458         if (tg && tg.transition) {
6459             Roo.log("waiting for the transitionend");
6460             return;
6461         }
6462         
6463         
6464         
6465         //Roo.log("fire event clicked");
6466         if(this.fireEvent('click', this, e) === false){
6467             return;
6468         };
6469         
6470         if(this.tagtype == 'span'){
6471             return;
6472         }
6473         
6474         //Roo.log(this.href);
6475         var ael = this.el.select('a',true).first();
6476         //Roo.log(ael);
6477         
6478         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6479             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6480             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6481                 return; // ignore... - it's a 'hash' to another page.
6482             }
6483             Roo.log("NavItem - prevent Default?");
6484             e.preventDefault();
6485             this.scrollToElement(e);
6486         }
6487         
6488         
6489         var p =  this.parent();
6490    
6491         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6492             if (typeof(p.setActiveItem) !== 'undefined') {
6493                 p.setActiveItem(this);
6494             }
6495         }
6496         
6497         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6498         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6499             // remove the collapsed menu expand...
6500             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6501         }
6502     },
6503     
6504     isActive: function () {
6505         return this.active
6506     },
6507     setActive : function(state, fire, is_was_active)
6508     {
6509         if (this.active && !state && this.navId) {
6510             this.was_active = true;
6511             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6512             if (nv) {
6513                 nv.clearWasActive(this);
6514             }
6515             
6516         }
6517         this.active = state;
6518         
6519         if (!state ) {
6520             this.el.removeClass('active');
6521             this.navLink ? this.navLink.removeClass('active') : false;
6522         } else if (!this.el.hasClass('active')) {
6523             
6524             this.el.addClass('active');
6525             if (Roo.bootstrap.version == 4 && this.navLink ) {
6526                 this.navLink.addClass('active');
6527             }
6528             
6529         }
6530         if (fire) {
6531             this.fireEvent('changed', this, state);
6532         }
6533         
6534         // show a panel if it's registered and related..
6535         
6536         if (!this.navId || !this.tabId || !state || is_was_active) {
6537             return;
6538         }
6539         
6540         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6541         if (!tg) {
6542             return;
6543         }
6544         var pan = tg.getPanelByName(this.tabId);
6545         if (!pan) {
6546             return;
6547         }
6548         // if we can not flip to new panel - go back to old nav highlight..
6549         if (false == tg.showPanel(pan)) {
6550             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6551             if (nv) {
6552                 var onav = nv.getWasActive();
6553                 if (onav) {
6554                     onav.setActive(true, false, true);
6555                 }
6556             }
6557             
6558         }
6559         
6560         
6561         
6562     },
6563      // this should not be here...
6564     setDisabled : function(state)
6565     {
6566         this.disabled = state;
6567         if (!state ) {
6568             this.el.removeClass('disabled');
6569         } else if (!this.el.hasClass('disabled')) {
6570             this.el.addClass('disabled');
6571         }
6572         
6573     },
6574     
6575     /**
6576      * Fetch the element to display the tooltip on.
6577      * @return {Roo.Element} defaults to this.el
6578      */
6579     tooltipEl : function()
6580     {
6581         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6582     },
6583     
6584     scrollToElement : function(e)
6585     {
6586         var c = document.body;
6587         
6588         /*
6589          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6590          */
6591         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6592             c = document.documentElement;
6593         }
6594         
6595         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6596         
6597         if(!target){
6598             return;
6599         }
6600
6601         var o = target.calcOffsetsTo(c);
6602         
6603         var options = {
6604             target : target,
6605             value : o[1]
6606         };
6607         
6608         this.fireEvent('scrollto', this, options, e);
6609         
6610         Roo.get(c).scrollTo('top', options.value, true);
6611         
6612         return;
6613     },
6614     /**
6615      * Set the HTML (text content) of the item
6616      * @param {string} html  content for the nav item
6617      */
6618     setHtml : function(html)
6619     {
6620         this.html = html;
6621         this.htmlEl.dom.innerHTML = html;
6622         
6623     } 
6624 });
6625  
6626
6627  /*
6628  * - LGPL
6629  *
6630  * sidebar item
6631  *
6632  *  li
6633  *    <span> icon </span>
6634  *    <span> text </span>
6635  *    <span>badge </span>
6636  */
6637
6638 /**
6639  * @class Roo.bootstrap.NavSidebarItem
6640  * @extends Roo.bootstrap.NavItem
6641  * Bootstrap Navbar.NavSidebarItem class
6642  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6643  * {Boolean} open is the menu open
6644  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6645  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6646  * {String} buttonSize (sm|md|lg)the extra classes for the button
6647  * {Boolean} showArrow show arrow next to the text (default true)
6648  * @constructor
6649  * Create a new Navbar Button
6650  * @param {Object} config The config object
6651  */
6652 Roo.bootstrap.NavSidebarItem = function(config){
6653     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6654     this.addEvents({
6655         // raw events
6656         /**
6657          * @event click
6658          * The raw click event for the entire grid.
6659          * @param {Roo.EventObject} e
6660          */
6661         "click" : true,
6662          /**
6663             * @event changed
6664             * Fires when the active item active state changes
6665             * @param {Roo.bootstrap.NavSidebarItem} this
6666             * @param {boolean} state the new state
6667              
6668          */
6669         'changed': true
6670     });
6671    
6672 };
6673
6674 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6675     
6676     badgeWeight : 'default',
6677     
6678     open: false,
6679     
6680     buttonView : false,
6681     
6682     buttonWeight : 'default',
6683     
6684     buttonSize : 'md',
6685     
6686     showArrow : true,
6687     
6688     getAutoCreate : function(){
6689         
6690         
6691         var a = {
6692                 tag: 'a',
6693                 href : this.href || '#',
6694                 cls: '',
6695                 html : '',
6696                 cn : []
6697         };
6698         
6699         if(this.buttonView){
6700             a = {
6701                 tag: 'button',
6702                 href : this.href || '#',
6703                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6704                 html : this.html,
6705                 cn : []
6706             };
6707         }
6708         
6709         var cfg = {
6710             tag: 'li',
6711             cls: '',
6712             cn: [ a ]
6713         };
6714         
6715         if (this.active) {
6716             cfg.cls += ' active';
6717         }
6718         
6719         if (this.disabled) {
6720             cfg.cls += ' disabled';
6721         }
6722         if (this.open) {
6723             cfg.cls += ' open x-open';
6724         }
6725         // left icon..
6726         if (this.glyphicon || this.icon) {
6727             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6728             a.cn.push({ tag : 'i', cls : c }) ;
6729         }
6730         
6731         if(!this.buttonView){
6732             var span = {
6733                 tag: 'span',
6734                 html : this.html || ''
6735             };
6736
6737             a.cn.push(span);
6738             
6739         }
6740         
6741         if (this.badge !== '') {
6742             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6743         }
6744         
6745         if (this.menu) {
6746             
6747             if(this.showArrow){
6748                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6749             }
6750             
6751             a.cls += ' dropdown-toggle treeview' ;
6752         }
6753         
6754         return cfg;
6755     },
6756     
6757     initEvents : function()
6758     { 
6759         if (typeof (this.menu) != 'undefined') {
6760             this.menu.parentType = this.xtype;
6761             this.menu.triggerEl = this.el;
6762             this.menu = this.addxtype(Roo.apply({}, this.menu));
6763         }
6764         
6765         this.el.on('click', this.onClick, this);
6766         
6767         if(this.badge !== ''){
6768             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6769         }
6770         
6771     },
6772     
6773     onClick : function(e)
6774     {
6775         if(this.disabled){
6776             e.preventDefault();
6777             return;
6778         }
6779         
6780         if(this.preventDefault){
6781             e.preventDefault();
6782         }
6783         
6784         this.fireEvent('click', this, e);
6785     },
6786     
6787     disable : function()
6788     {
6789         this.setDisabled(true);
6790     },
6791     
6792     enable : function()
6793     {
6794         this.setDisabled(false);
6795     },
6796     
6797     setDisabled : function(state)
6798     {
6799         if(this.disabled == state){
6800             return;
6801         }
6802         
6803         this.disabled = state;
6804         
6805         if (state) {
6806             this.el.addClass('disabled');
6807             return;
6808         }
6809         
6810         this.el.removeClass('disabled');
6811         
6812         return;
6813     },
6814     
6815     setActive : function(state)
6816     {
6817         if(this.active == state){
6818             return;
6819         }
6820         
6821         this.active = state;
6822         
6823         if (state) {
6824             this.el.addClass('active');
6825             return;
6826         }
6827         
6828         this.el.removeClass('active');
6829         
6830         return;
6831     },
6832     
6833     isActive: function () 
6834     {
6835         return this.active;
6836     },
6837     
6838     setBadge : function(str)
6839     {
6840         if(!this.badgeEl){
6841             return;
6842         }
6843         
6844         this.badgeEl.dom.innerHTML = str;
6845     }
6846     
6847    
6848      
6849  
6850 });
6851  
6852
6853  /*
6854  * - LGPL
6855  *
6856  *  Breadcrumb Nav
6857  * 
6858  */
6859 Roo.namespace('Roo.bootstrap.breadcrumb');
6860
6861
6862 /**
6863  * @class Roo.bootstrap.breadcrumb.Nav
6864  * @extends Roo.bootstrap.Component
6865  * Bootstrap Breadcrumb Nav Class
6866  *  
6867  * @children Roo.bootstrap.breadcrumb.Item
6868  * 
6869  * @constructor
6870  * Create a new breadcrumb.Nav
6871  * @param {Object} config The config object
6872  */
6873
6874
6875 Roo.bootstrap.breadcrumb.Nav = function(config){
6876     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6877     
6878     
6879 };
6880
6881 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6882     
6883     getAutoCreate : function()
6884     {
6885
6886         var cfg = {
6887             tag: 'nav',
6888             cn : [
6889                 {
6890                     tag : 'ol',
6891                     cls : 'breadcrumb'
6892                 }
6893             ]
6894             
6895         };
6896           
6897         return cfg;
6898     },
6899     
6900     initEvents: function()
6901     {
6902         this.olEl = this.el.select('ol',true).first();    
6903     },
6904     getChildContainer : function()
6905     {
6906         return this.olEl;  
6907     }
6908     
6909 });
6910
6911  /*
6912  * - LGPL
6913  *
6914  *  Breadcrumb Item
6915  * 
6916  */
6917
6918
6919 /**
6920  * @class Roo.bootstrap.breadcrumb.Nav
6921  * @extends Roo.bootstrap.Component
6922  * Bootstrap Breadcrumb Nav Class
6923  *  
6924  * @children Roo.bootstrap.breadcrumb.Component
6925  * @cfg {String} html the content of the link.
6926  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6927  * @cfg {Boolean} active is it active
6928
6929  * 
6930  * @constructor
6931  * Create a new breadcrumb.Nav
6932  * @param {Object} config The config object
6933  */
6934
6935 Roo.bootstrap.breadcrumb.Item = function(config){
6936     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6937     this.addEvents({
6938         // img events
6939         /**
6940          * @event click
6941          * The img click event for the img.
6942          * @param {Roo.EventObject} e
6943          */
6944         "click" : true
6945     });
6946     
6947 };
6948
6949 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6950     
6951     href: false,
6952     html : '',
6953     
6954     getAutoCreate : function()
6955     {
6956
6957         var cfg = {
6958             tag: 'li',
6959             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6960         };
6961         if (this.href !== false) {
6962             cfg.cn = [{
6963                 tag : 'a',
6964                 href : this.href,
6965                 html : this.html
6966             }];
6967         } else {
6968             cfg.html = this.html;
6969         }
6970         
6971         return cfg;
6972     },
6973     
6974     initEvents: function()
6975     {
6976         if (this.href) {
6977             this.el.select('a', true).first().on('click',this.onClick, this)
6978         }
6979         
6980     },
6981     onClick : function(e)
6982     {
6983         e.preventDefault();
6984         this.fireEvent('click',this,  e);
6985     }
6986     
6987 });
6988
6989  /*
6990  * - LGPL
6991  *
6992  * row
6993  * 
6994  */
6995
6996 /**
6997  * @class Roo.bootstrap.Row
6998  * @extends Roo.bootstrap.Component
6999  * Bootstrap Row class (contains columns...)
7000  * 
7001  * @constructor
7002  * Create a new Row
7003  * @param {Object} config The config object
7004  */
7005
7006 Roo.bootstrap.Row = function(config){
7007     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7008 };
7009
7010 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7011     
7012     getAutoCreate : function(){
7013        return {
7014             cls: 'row clearfix'
7015        };
7016     }
7017     
7018     
7019 });
7020
7021  
7022
7023  /*
7024  * - LGPL
7025  *
7026  * pagination
7027  * 
7028  */
7029
7030 /**
7031  * @class Roo.bootstrap.Pagination
7032  * @extends Roo.bootstrap.Component
7033  * Bootstrap Pagination class
7034  * @cfg {String} size xs | sm | md | lg
7035  * @cfg {Boolean} inverse false | true
7036  * 
7037  * @constructor
7038  * Create a new Pagination
7039  * @param {Object} config The config object
7040  */
7041
7042 Roo.bootstrap.Pagination = function(config){
7043     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7044 };
7045
7046 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7047     
7048     cls: false,
7049     size: false,
7050     inverse: false,
7051     
7052     getAutoCreate : function(){
7053         var cfg = {
7054             tag: 'ul',
7055                 cls: 'pagination'
7056         };
7057         if (this.inverse) {
7058             cfg.cls += ' inverse';
7059         }
7060         if (this.html) {
7061             cfg.html=this.html;
7062         }
7063         if (this.cls) {
7064             cfg.cls += " " + this.cls;
7065         }
7066         return cfg;
7067     }
7068    
7069 });
7070
7071  
7072
7073  /*
7074  * - LGPL
7075  *
7076  * Pagination item
7077  * 
7078  */
7079
7080
7081 /**
7082  * @class Roo.bootstrap.PaginationItem
7083  * @extends Roo.bootstrap.Component
7084  * Bootstrap PaginationItem class
7085  * @cfg {String} html text
7086  * @cfg {String} href the link
7087  * @cfg {Boolean} preventDefault (true | false) default true
7088  * @cfg {Boolean} active (true | false) default false
7089  * @cfg {Boolean} disabled default false
7090  * 
7091  * 
7092  * @constructor
7093  * Create a new PaginationItem
7094  * @param {Object} config The config object
7095  */
7096
7097
7098 Roo.bootstrap.PaginationItem = function(config){
7099     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7100     this.addEvents({
7101         // raw events
7102         /**
7103          * @event click
7104          * The raw click event for the entire grid.
7105          * @param {Roo.EventObject} e
7106          */
7107         "click" : true
7108     });
7109 };
7110
7111 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7112     
7113     href : false,
7114     html : false,
7115     preventDefault: true,
7116     active : false,
7117     cls : false,
7118     disabled: false,
7119     
7120     getAutoCreate : function(){
7121         var cfg= {
7122             tag: 'li',
7123             cn: [
7124                 {
7125                     tag : 'a',
7126                     href : this.href ? this.href : '#',
7127                     html : this.html ? this.html : ''
7128                 }
7129             ]
7130         };
7131         
7132         if(this.cls){
7133             cfg.cls = this.cls;
7134         }
7135         
7136         if(this.disabled){
7137             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7138         }
7139         
7140         if(this.active){
7141             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7142         }
7143         
7144         return cfg;
7145     },
7146     
7147     initEvents: function() {
7148         
7149         this.el.on('click', this.onClick, this);
7150         
7151     },
7152     onClick : function(e)
7153     {
7154         Roo.log('PaginationItem on click ');
7155         if(this.preventDefault){
7156             e.preventDefault();
7157         }
7158         
7159         if(this.disabled){
7160             return;
7161         }
7162         
7163         this.fireEvent('click', this, e);
7164     }
7165    
7166 });
7167
7168  
7169
7170  /*
7171  * - LGPL
7172  *
7173  * slider
7174  * 
7175  */
7176
7177
7178 /**
7179  * @class Roo.bootstrap.Slider
7180  * @extends Roo.bootstrap.Component
7181  * Bootstrap Slider class
7182  *    
7183  * @constructor
7184  * Create a new Slider
7185  * @param {Object} config The config object
7186  */
7187
7188 Roo.bootstrap.Slider = function(config){
7189     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7190 };
7191
7192 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7193     
7194     getAutoCreate : function(){
7195         
7196         var cfg = {
7197             tag: 'div',
7198             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7199             cn: [
7200                 {
7201                     tag: 'a',
7202                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7203                 }
7204             ]
7205         };
7206         
7207         return cfg;
7208     }
7209    
7210 });
7211
7212  /*
7213  * Based on:
7214  * Ext JS Library 1.1.1
7215  * Copyright(c) 2006-2007, Ext JS, LLC.
7216  *
7217  * Originally Released Under LGPL - original licence link has changed is not relivant.
7218  *
7219  * Fork - LGPL
7220  * <script type="text/javascript">
7221  */
7222  
7223
7224 /**
7225  * @class Roo.grid.ColumnModel
7226  * @extends Roo.util.Observable
7227  * This is the default implementation of a ColumnModel used by the Grid. It defines
7228  * the columns in the grid.
7229  * <br>Usage:<br>
7230  <pre><code>
7231  var colModel = new Roo.grid.ColumnModel([
7232         {header: "Ticker", width: 60, sortable: true, locked: true},
7233         {header: "Company Name", width: 150, sortable: true},
7234         {header: "Market Cap.", width: 100, sortable: true},
7235         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7236         {header: "Employees", width: 100, sortable: true, resizable: false}
7237  ]);
7238  </code></pre>
7239  * <p>
7240  
7241  * The config options listed for this class are options which may appear in each
7242  * individual column definition.
7243  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7244  * @constructor
7245  * @param {Object} config An Array of column config objects. See this class's
7246  * config objects for details.
7247 */
7248 Roo.grid.ColumnModel = function(config){
7249         /**
7250      * The config passed into the constructor
7251      */
7252     this.config = config;
7253     this.lookup = {};
7254
7255     // if no id, create one
7256     // if the column does not have a dataIndex mapping,
7257     // map it to the order it is in the config
7258     for(var i = 0, len = config.length; i < len; i++){
7259         var c = config[i];
7260         if(typeof c.dataIndex == "undefined"){
7261             c.dataIndex = i;
7262         }
7263         if(typeof c.renderer == "string"){
7264             c.renderer = Roo.util.Format[c.renderer];
7265         }
7266         if(typeof c.id == "undefined"){
7267             c.id = Roo.id();
7268         }
7269         if(c.editor && c.editor.xtype){
7270             c.editor  = Roo.factory(c.editor, Roo.grid);
7271         }
7272         if(c.editor && c.editor.isFormField){
7273             c.editor = new Roo.grid.GridEditor(c.editor);
7274         }
7275         this.lookup[c.id] = c;
7276     }
7277
7278     /**
7279      * The width of columns which have no width specified (defaults to 100)
7280      * @type Number
7281      */
7282     this.defaultWidth = 100;
7283
7284     /**
7285      * Default sortable of columns which have no sortable specified (defaults to false)
7286      * @type Boolean
7287      */
7288     this.defaultSortable = false;
7289
7290     this.addEvents({
7291         /**
7292              * @event widthchange
7293              * Fires when the width of a column changes.
7294              * @param {ColumnModel} this
7295              * @param {Number} columnIndex The column index
7296              * @param {Number} newWidth The new width
7297              */
7298             "widthchange": true,
7299         /**
7300              * @event headerchange
7301              * Fires when the text of a header changes.
7302              * @param {ColumnModel} this
7303              * @param {Number} columnIndex The column index
7304              * @param {Number} newText The new header text
7305              */
7306             "headerchange": true,
7307         /**
7308              * @event hiddenchange
7309              * Fires when a column is hidden or "unhidden".
7310              * @param {ColumnModel} this
7311              * @param {Number} columnIndex The column index
7312              * @param {Boolean} hidden true if hidden, false otherwise
7313              */
7314             "hiddenchange": true,
7315             /**
7316          * @event columnmoved
7317          * Fires when a column is moved.
7318          * @param {ColumnModel} this
7319          * @param {Number} oldIndex
7320          * @param {Number} newIndex
7321          */
7322         "columnmoved" : true,
7323         /**
7324          * @event columlockchange
7325          * Fires when a column's locked state is changed
7326          * @param {ColumnModel} this
7327          * @param {Number} colIndex
7328          * @param {Boolean} locked true if locked
7329          */
7330         "columnlockchange" : true
7331     });
7332     Roo.grid.ColumnModel.superclass.constructor.call(this);
7333 };
7334 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7335     /**
7336      * @cfg {String} header The header text to display in the Grid view.
7337      */
7338     /**
7339      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7340      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7341      * specified, the column's index is used as an index into the Record's data Array.
7342      */
7343     /**
7344      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7345      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7346      */
7347     /**
7348      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7349      * Defaults to the value of the {@link #defaultSortable} property.
7350      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7351      */
7352     /**
7353      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7354      */
7355     /**
7356      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7357      */
7358     /**
7359      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7360      */
7361     /**
7362      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7363      */
7364     /**
7365      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7366      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7367      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7368      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7369      */
7370        /**
7371      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7372      */
7373     /**
7374      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7375      */
7376     /**
7377      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7378      */
7379     /**
7380      * @cfg {String} cursor (Optional)
7381      */
7382     /**
7383      * @cfg {String} tooltip (Optional)
7384      */
7385     /**
7386      * @cfg {Number} xs (Optional)
7387      */
7388     /**
7389      * @cfg {Number} sm (Optional)
7390      */
7391     /**
7392      * @cfg {Number} md (Optional)
7393      */
7394     /**
7395      * @cfg {Number} lg (Optional)
7396      */
7397     /**
7398      * Returns the id of the column at the specified index.
7399      * @param {Number} index The column index
7400      * @return {String} the id
7401      */
7402     getColumnId : function(index){
7403         return this.config[index].id;
7404     },
7405
7406     /**
7407      * Returns the column for a specified id.
7408      * @param {String} id The column id
7409      * @return {Object} the column
7410      */
7411     getColumnById : function(id){
7412         return this.lookup[id];
7413     },
7414
7415     
7416     /**
7417      * Returns the column for a specified dataIndex.
7418      * @param {String} dataIndex The column dataIndex
7419      * @return {Object|Boolean} the column or false if not found
7420      */
7421     getColumnByDataIndex: function(dataIndex){
7422         var index = this.findColumnIndex(dataIndex);
7423         return index > -1 ? this.config[index] : false;
7424     },
7425     
7426     /**
7427      * Returns the index for a specified column id.
7428      * @param {String} id The column id
7429      * @return {Number} the index, or -1 if not found
7430      */
7431     getIndexById : function(id){
7432         for(var i = 0, len = this.config.length; i < len; i++){
7433             if(this.config[i].id == id){
7434                 return i;
7435             }
7436         }
7437         return -1;
7438     },
7439     
7440     /**
7441      * Returns the index for a specified column dataIndex.
7442      * @param {String} dataIndex The column dataIndex
7443      * @return {Number} the index, or -1 if not found
7444      */
7445     
7446     findColumnIndex : function(dataIndex){
7447         for(var i = 0, len = this.config.length; i < len; i++){
7448             if(this.config[i].dataIndex == dataIndex){
7449                 return i;
7450             }
7451         }
7452         return -1;
7453     },
7454     
7455     
7456     moveColumn : function(oldIndex, newIndex){
7457         var c = this.config[oldIndex];
7458         this.config.splice(oldIndex, 1);
7459         this.config.splice(newIndex, 0, c);
7460         this.dataMap = null;
7461         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7462     },
7463
7464     isLocked : function(colIndex){
7465         return this.config[colIndex].locked === true;
7466     },
7467
7468     setLocked : function(colIndex, value, suppressEvent){
7469         if(this.isLocked(colIndex) == value){
7470             return;
7471         }
7472         this.config[colIndex].locked = value;
7473         if(!suppressEvent){
7474             this.fireEvent("columnlockchange", this, colIndex, value);
7475         }
7476     },
7477
7478     getTotalLockedWidth : function(){
7479         var totalWidth = 0;
7480         for(var i = 0; i < this.config.length; i++){
7481             if(this.isLocked(i) && !this.isHidden(i)){
7482                 this.totalWidth += this.getColumnWidth(i);
7483             }
7484         }
7485         return totalWidth;
7486     },
7487
7488     getLockedCount : function(){
7489         for(var i = 0, len = this.config.length; i < len; i++){
7490             if(!this.isLocked(i)){
7491                 return i;
7492             }
7493         }
7494         
7495         return this.config.length;
7496     },
7497
7498     /**
7499      * Returns the number of columns.
7500      * @return {Number}
7501      */
7502     getColumnCount : function(visibleOnly){
7503         if(visibleOnly === true){
7504             var c = 0;
7505             for(var i = 0, len = this.config.length; i < len; i++){
7506                 if(!this.isHidden(i)){
7507                     c++;
7508                 }
7509             }
7510             return c;
7511         }
7512         return this.config.length;
7513     },
7514
7515     /**
7516      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7517      * @param {Function} fn
7518      * @param {Object} scope (optional)
7519      * @return {Array} result
7520      */
7521     getColumnsBy : function(fn, scope){
7522         var r = [];
7523         for(var i = 0, len = this.config.length; i < len; i++){
7524             var c = this.config[i];
7525             if(fn.call(scope||this, c, i) === true){
7526                 r[r.length] = c;
7527             }
7528         }
7529         return r;
7530     },
7531
7532     /**
7533      * Returns true if the specified column is sortable.
7534      * @param {Number} col The column index
7535      * @return {Boolean}
7536      */
7537     isSortable : function(col){
7538         if(typeof this.config[col].sortable == "undefined"){
7539             return this.defaultSortable;
7540         }
7541         return this.config[col].sortable;
7542     },
7543
7544     /**
7545      * Returns the rendering (formatting) function defined for the column.
7546      * @param {Number} col The column index.
7547      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7548      */
7549     getRenderer : function(col){
7550         if(!this.config[col].renderer){
7551             return Roo.grid.ColumnModel.defaultRenderer;
7552         }
7553         return this.config[col].renderer;
7554     },
7555
7556     /**
7557      * Sets the rendering (formatting) function for a column.
7558      * @param {Number} col The column index
7559      * @param {Function} fn The function to use to process the cell's raw data
7560      * to return HTML markup for the grid view. The render function is called with
7561      * the following parameters:<ul>
7562      * <li>Data value.</li>
7563      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7564      * <li>css A CSS style string to apply to the table cell.</li>
7565      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7566      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7567      * <li>Row index</li>
7568      * <li>Column index</li>
7569      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7570      */
7571     setRenderer : function(col, fn){
7572         this.config[col].renderer = fn;
7573     },
7574
7575     /**
7576      * Returns the width for the specified column.
7577      * @param {Number} col The column index
7578      * @return {Number}
7579      */
7580     getColumnWidth : function(col){
7581         return this.config[col].width * 1 || this.defaultWidth;
7582     },
7583
7584     /**
7585      * Sets the width for a column.
7586      * @param {Number} col The column index
7587      * @param {Number} width The new width
7588      */
7589     setColumnWidth : function(col, width, suppressEvent){
7590         this.config[col].width = width;
7591         this.totalWidth = null;
7592         if(!suppressEvent){
7593              this.fireEvent("widthchange", this, col, width);
7594         }
7595     },
7596
7597     /**
7598      * Returns the total width of all columns.
7599      * @param {Boolean} includeHidden True to include hidden column widths
7600      * @return {Number}
7601      */
7602     getTotalWidth : function(includeHidden){
7603         if(!this.totalWidth){
7604             this.totalWidth = 0;
7605             for(var i = 0, len = this.config.length; i < len; i++){
7606                 if(includeHidden || !this.isHidden(i)){
7607                     this.totalWidth += this.getColumnWidth(i);
7608                 }
7609             }
7610         }
7611         return this.totalWidth;
7612     },
7613
7614     /**
7615      * Returns the header for the specified column.
7616      * @param {Number} col The column index
7617      * @return {String}
7618      */
7619     getColumnHeader : function(col){
7620         return this.config[col].header;
7621     },
7622
7623     /**
7624      * Sets the header for a column.
7625      * @param {Number} col The column index
7626      * @param {String} header The new header
7627      */
7628     setColumnHeader : function(col, header){
7629         this.config[col].header = header;
7630         this.fireEvent("headerchange", this, col, header);
7631     },
7632
7633     /**
7634      * Returns the tooltip for the specified column.
7635      * @param {Number} col The column index
7636      * @return {String}
7637      */
7638     getColumnTooltip : function(col){
7639             return this.config[col].tooltip;
7640     },
7641     /**
7642      * Sets the tooltip for a column.
7643      * @param {Number} col The column index
7644      * @param {String} tooltip The new tooltip
7645      */
7646     setColumnTooltip : function(col, tooltip){
7647             this.config[col].tooltip = tooltip;
7648     },
7649
7650     /**
7651      * Returns the dataIndex for the specified column.
7652      * @param {Number} col The column index
7653      * @return {Number}
7654      */
7655     getDataIndex : function(col){
7656         return this.config[col].dataIndex;
7657     },
7658
7659     /**
7660      * Sets the dataIndex for a column.
7661      * @param {Number} col The column index
7662      * @param {Number} dataIndex The new dataIndex
7663      */
7664     setDataIndex : function(col, dataIndex){
7665         this.config[col].dataIndex = dataIndex;
7666     },
7667
7668     
7669     
7670     /**
7671      * Returns true if the cell is editable.
7672      * @param {Number} colIndex The column index
7673      * @param {Number} rowIndex The row index - this is nto actually used..?
7674      * @return {Boolean}
7675      */
7676     isCellEditable : function(colIndex, rowIndex){
7677         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7678     },
7679
7680     /**
7681      * Returns the editor defined for the cell/column.
7682      * return false or null to disable editing.
7683      * @param {Number} colIndex The column index
7684      * @param {Number} rowIndex The row index
7685      * @return {Object}
7686      */
7687     getCellEditor : function(colIndex, rowIndex){
7688         return this.config[colIndex].editor;
7689     },
7690
7691     /**
7692      * Sets if a column is editable.
7693      * @param {Number} col The column index
7694      * @param {Boolean} editable True if the column is editable
7695      */
7696     setEditable : function(col, editable){
7697         this.config[col].editable = editable;
7698     },
7699
7700
7701     /**
7702      * Returns true if the column is hidden.
7703      * @param {Number} colIndex The column index
7704      * @return {Boolean}
7705      */
7706     isHidden : function(colIndex){
7707         return this.config[colIndex].hidden;
7708     },
7709
7710
7711     /**
7712      * Returns true if the column width cannot be changed
7713      */
7714     isFixed : function(colIndex){
7715         return this.config[colIndex].fixed;
7716     },
7717
7718     /**
7719      * Returns true if the column can be resized
7720      * @return {Boolean}
7721      */
7722     isResizable : function(colIndex){
7723         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7724     },
7725     /**
7726      * Sets if a column is hidden.
7727      * @param {Number} colIndex The column index
7728      * @param {Boolean} hidden True if the column is hidden
7729      */
7730     setHidden : function(colIndex, hidden){
7731         this.config[colIndex].hidden = hidden;
7732         this.totalWidth = null;
7733         this.fireEvent("hiddenchange", this, colIndex, hidden);
7734     },
7735
7736     /**
7737      * Sets the editor for a column.
7738      * @param {Number} col The column index
7739      * @param {Object} editor The editor object
7740      */
7741     setEditor : function(col, editor){
7742         this.config[col].editor = editor;
7743     }
7744 });
7745
7746 Roo.grid.ColumnModel.defaultRenderer = function(value)
7747 {
7748     if(typeof value == "object") {
7749         return value;
7750     }
7751         if(typeof value == "string" && value.length < 1){
7752             return "&#160;";
7753         }
7754     
7755         return String.format("{0}", value);
7756 };
7757
7758 // Alias for backwards compatibility
7759 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7760 /*
7761  * Based on:
7762  * Ext JS Library 1.1.1
7763  * Copyright(c) 2006-2007, Ext JS, LLC.
7764  *
7765  * Originally Released Under LGPL - original licence link has changed is not relivant.
7766  *
7767  * Fork - LGPL
7768  * <script type="text/javascript">
7769  */
7770  
7771 /**
7772  * @class Roo.LoadMask
7773  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7774  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7775  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7776  * element's UpdateManager load indicator and will be destroyed after the initial load.
7777  * @constructor
7778  * Create a new LoadMask
7779  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7780  * @param {Object} config The config object
7781  */
7782 Roo.LoadMask = function(el, config){
7783     this.el = Roo.get(el);
7784     Roo.apply(this, config);
7785     if(this.store){
7786         this.store.on('beforeload', this.onBeforeLoad, this);
7787         this.store.on('load', this.onLoad, this);
7788         this.store.on('loadexception', this.onLoadException, this);
7789         this.removeMask = false;
7790     }else{
7791         var um = this.el.getUpdateManager();
7792         um.showLoadIndicator = false; // disable the default indicator
7793         um.on('beforeupdate', this.onBeforeLoad, this);
7794         um.on('update', this.onLoad, this);
7795         um.on('failure', this.onLoad, this);
7796         this.removeMask = true;
7797     }
7798 };
7799
7800 Roo.LoadMask.prototype = {
7801     /**
7802      * @cfg {Boolean} removeMask
7803      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7804      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7805      */
7806     /**
7807      * @cfg {String} msg
7808      * The text to display in a centered loading message box (defaults to 'Loading...')
7809      */
7810     msg : 'Loading...',
7811     /**
7812      * @cfg {String} msgCls
7813      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7814      */
7815     msgCls : 'x-mask-loading',
7816
7817     /**
7818      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7819      * @type Boolean
7820      */
7821     disabled: false,
7822
7823     /**
7824      * Disables the mask to prevent it from being displayed
7825      */
7826     disable : function(){
7827        this.disabled = true;
7828     },
7829
7830     /**
7831      * Enables the mask so that it can be displayed
7832      */
7833     enable : function(){
7834         this.disabled = false;
7835     },
7836     
7837     onLoadException : function()
7838     {
7839         Roo.log(arguments);
7840         
7841         if (typeof(arguments[3]) != 'undefined') {
7842             Roo.MessageBox.alert("Error loading",arguments[3]);
7843         } 
7844         /*
7845         try {
7846             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7847                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7848             }   
7849         } catch(e) {
7850             
7851         }
7852         */
7853     
7854         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7855     },
7856     // private
7857     onLoad : function()
7858     {
7859         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7860     },
7861
7862     // private
7863     onBeforeLoad : function(){
7864         if(!this.disabled){
7865             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7866         }
7867     },
7868
7869     // private
7870     destroy : function(){
7871         if(this.store){
7872             this.store.un('beforeload', this.onBeforeLoad, this);
7873             this.store.un('load', this.onLoad, this);
7874             this.store.un('loadexception', this.onLoadException, this);
7875         }else{
7876             var um = this.el.getUpdateManager();
7877             um.un('beforeupdate', this.onBeforeLoad, this);
7878             um.un('update', this.onLoad, this);
7879             um.un('failure', this.onLoad, this);
7880         }
7881     }
7882 };/*
7883  * - LGPL
7884  *
7885  * table
7886  * 
7887  */
7888
7889 /**
7890  * @class Roo.bootstrap.Table
7891  * @extends Roo.bootstrap.Component
7892  * Bootstrap Table class
7893  * @cfg {String} cls table class
7894  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7895  * @cfg {String} bgcolor Specifies the background color for a table
7896  * @cfg {Number} border Specifies whether the table cells should have borders or not
7897  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7898  * @cfg {Number} cellspacing Specifies the space between cells
7899  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7900  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7901  * @cfg {String} sortable Specifies that the table should be sortable
7902  * @cfg {String} summary Specifies a summary of the content of a table
7903  * @cfg {Number} width Specifies the width of a table
7904  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7905  * 
7906  * @cfg {boolean} striped Should the rows be alternative striped
7907  * @cfg {boolean} bordered Add borders to the table
7908  * @cfg {boolean} hover Add hover highlighting
7909  * @cfg {boolean} condensed Format condensed
7910  * @cfg {boolean} responsive Format condensed
7911  * @cfg {Boolean} loadMask (true|false) default false
7912  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7913  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7914  * @cfg {Boolean} rowSelection (true|false) default false
7915  * @cfg {Boolean} cellSelection (true|false) default false
7916  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7917  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7918  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7919  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7920  
7921  * 
7922  * @constructor
7923  * Create a new Table
7924  * @param {Object} config The config object
7925  */
7926
7927 Roo.bootstrap.Table = function(config){
7928     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7929     
7930   
7931     
7932     // BC...
7933     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7934     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7935     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7936     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7937     
7938     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7939     if (this.sm) {
7940         this.sm.grid = this;
7941         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7942         this.sm = this.selModel;
7943         this.sm.xmodule = this.xmodule || false;
7944     }
7945     
7946     if (this.cm && typeof(this.cm.config) == 'undefined') {
7947         this.colModel = new Roo.grid.ColumnModel(this.cm);
7948         this.cm = this.colModel;
7949         this.cm.xmodule = this.xmodule || false;
7950     }
7951     if (this.store) {
7952         this.store= Roo.factory(this.store, Roo.data);
7953         this.ds = this.store;
7954         this.ds.xmodule = this.xmodule || false;
7955          
7956     }
7957     if (this.footer && this.store) {
7958         this.footer.dataSource = this.ds;
7959         this.footer = Roo.factory(this.footer);
7960     }
7961     
7962     /** @private */
7963     this.addEvents({
7964         /**
7965          * @event cellclick
7966          * Fires when a cell is clicked
7967          * @param {Roo.bootstrap.Table} this
7968          * @param {Roo.Element} el
7969          * @param {Number} rowIndex
7970          * @param {Number} columnIndex
7971          * @param {Roo.EventObject} e
7972          */
7973         "cellclick" : true,
7974         /**
7975          * @event celldblclick
7976          * Fires when a cell is double clicked
7977          * @param {Roo.bootstrap.Table} this
7978          * @param {Roo.Element} el
7979          * @param {Number} rowIndex
7980          * @param {Number} columnIndex
7981          * @param {Roo.EventObject} e
7982          */
7983         "celldblclick" : true,
7984         /**
7985          * @event rowclick
7986          * Fires when a row is clicked
7987          * @param {Roo.bootstrap.Table} this
7988          * @param {Roo.Element} el
7989          * @param {Number} rowIndex
7990          * @param {Roo.EventObject} e
7991          */
7992         "rowclick" : true,
7993         /**
7994          * @event rowdblclick
7995          * Fires when a row is double clicked
7996          * @param {Roo.bootstrap.Table} this
7997          * @param {Roo.Element} el
7998          * @param {Number} rowIndex
7999          * @param {Roo.EventObject} e
8000          */
8001         "rowdblclick" : true,
8002         /**
8003          * @event mouseover
8004          * Fires when a mouseover occur
8005          * @param {Roo.bootstrap.Table} this
8006          * @param {Roo.Element} el
8007          * @param {Number} rowIndex
8008          * @param {Number} columnIndex
8009          * @param {Roo.EventObject} e
8010          */
8011         "mouseover" : true,
8012         /**
8013          * @event mouseout
8014          * Fires when a mouseout occur
8015          * @param {Roo.bootstrap.Table} this
8016          * @param {Roo.Element} el
8017          * @param {Number} rowIndex
8018          * @param {Number} columnIndex
8019          * @param {Roo.EventObject} e
8020          */
8021         "mouseout" : true,
8022         /**
8023          * @event rowclass
8024          * Fires when a row is rendered, so you can change add a style to it.
8025          * @param {Roo.bootstrap.Table} this
8026          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8027          */
8028         'rowclass' : true,
8029           /**
8030          * @event rowsrendered
8031          * Fires when all the  rows have been rendered
8032          * @param {Roo.bootstrap.Table} this
8033          */
8034         'rowsrendered' : true,
8035         /**
8036          * @event contextmenu
8037          * The raw contextmenu event for the entire grid.
8038          * @param {Roo.EventObject} e
8039          */
8040         "contextmenu" : true,
8041         /**
8042          * @event rowcontextmenu
8043          * Fires when a row is right clicked
8044          * @param {Roo.bootstrap.Table} this
8045          * @param {Number} rowIndex
8046          * @param {Roo.EventObject} e
8047          */
8048         "rowcontextmenu" : true,
8049         /**
8050          * @event cellcontextmenu
8051          * Fires when a cell is right clicked
8052          * @param {Roo.bootstrap.Table} this
8053          * @param {Number} rowIndex
8054          * @param {Number} cellIndex
8055          * @param {Roo.EventObject} e
8056          */
8057          "cellcontextmenu" : true,
8058          /**
8059          * @event headercontextmenu
8060          * Fires when a header is right clicked
8061          * @param {Roo.bootstrap.Table} this
8062          * @param {Number} columnIndex
8063          * @param {Roo.EventObject} e
8064          */
8065         "headercontextmenu" : true
8066     });
8067 };
8068
8069 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8070     
8071     cls: false,
8072     align: false,
8073     bgcolor: false,
8074     border: false,
8075     cellpadding: false,
8076     cellspacing: false,
8077     frame: false,
8078     rules: false,
8079     sortable: false,
8080     summary: false,
8081     width: false,
8082     striped : false,
8083     scrollBody : false,
8084     bordered: false,
8085     hover:  false,
8086     condensed : false,
8087     responsive : false,
8088     sm : false,
8089     cm : false,
8090     store : false,
8091     loadMask : false,
8092     footerShow : true,
8093     headerShow : true,
8094   
8095     rowSelection : false,
8096     cellSelection : false,
8097     layout : false,
8098     
8099     // Roo.Element - the tbody
8100     mainBody: false,
8101     // Roo.Element - thead element
8102     mainHead: false,
8103     
8104     container: false, // used by gridpanel...
8105     
8106     lazyLoad : false,
8107     
8108     CSS : Roo.util.CSS,
8109     
8110     auto_hide_footer : false,
8111     
8112     getAutoCreate : function()
8113     {
8114         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8115         
8116         cfg = {
8117             tag: 'table',
8118             cls : 'table',
8119             cn : []
8120         };
8121         if (this.scrollBody) {
8122             cfg.cls += ' table-body-fixed';
8123         }    
8124         if (this.striped) {
8125             cfg.cls += ' table-striped';
8126         }
8127         
8128         if (this.hover) {
8129             cfg.cls += ' table-hover';
8130         }
8131         if (this.bordered) {
8132             cfg.cls += ' table-bordered';
8133         }
8134         if (this.condensed) {
8135             cfg.cls += ' table-condensed';
8136         }
8137         if (this.responsive) {
8138             cfg.cls += ' table-responsive';
8139         }
8140         
8141         if (this.cls) {
8142             cfg.cls+=  ' ' +this.cls;
8143         }
8144         
8145         // this lot should be simplifed...
8146         var _t = this;
8147         var cp = [
8148             'align',
8149             'bgcolor',
8150             'border',
8151             'cellpadding',
8152             'cellspacing',
8153             'frame',
8154             'rules',
8155             'sortable',
8156             'summary',
8157             'width'
8158         ].forEach(function(k) {
8159             if (_t[k]) {
8160                 cfg[k] = _t[k];
8161             }
8162         });
8163         
8164         
8165         if (this.layout) {
8166             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8167         }
8168         
8169         if(this.store || this.cm){
8170             if(this.headerShow){
8171                 cfg.cn.push(this.renderHeader());
8172             }
8173             
8174             cfg.cn.push(this.renderBody());
8175             
8176             if(this.footerShow){
8177                 cfg.cn.push(this.renderFooter());
8178             }
8179             // where does this come from?
8180             //cfg.cls+=  ' TableGrid';
8181         }
8182         
8183         return { cn : [ cfg ] };
8184     },
8185     
8186     initEvents : function()
8187     {   
8188         if(!this.store || !this.cm){
8189             return;
8190         }
8191         if (this.selModel) {
8192             this.selModel.initEvents();
8193         }
8194         
8195         
8196         //Roo.log('initEvents with ds!!!!');
8197         
8198         this.mainBody = this.el.select('tbody', true).first();
8199         this.mainHead = this.el.select('thead', true).first();
8200         this.mainFoot = this.el.select('tfoot', true).first();
8201         
8202         
8203         
8204         var _this = this;
8205         
8206         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8207             e.on('click', _this.sort, _this);
8208         });
8209         
8210         this.mainBody.on("click", this.onClick, this);
8211         this.mainBody.on("dblclick", this.onDblClick, this);
8212         
8213         // why is this done????? = it breaks dialogs??
8214         //this.parent().el.setStyle('position', 'relative');
8215         
8216         
8217         if (this.footer) {
8218             this.footer.parentId = this.id;
8219             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8220             
8221             if(this.lazyLoad){
8222                 this.el.select('tfoot tr td').first().addClass('hide');
8223             }
8224         } 
8225         
8226         if(this.loadMask) {
8227             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8228         }
8229         
8230         this.store.on('load', this.onLoad, this);
8231         this.store.on('beforeload', this.onBeforeLoad, this);
8232         this.store.on('update', this.onUpdate, this);
8233         this.store.on('add', this.onAdd, this);
8234         this.store.on("clear", this.clear, this);
8235         
8236         this.el.on("contextmenu", this.onContextMenu, this);
8237         
8238         this.mainBody.on('scroll', this.onBodyScroll, this);
8239         
8240         this.cm.on("headerchange", this.onHeaderChange, this);
8241         
8242         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8243         
8244     },
8245     
8246     onContextMenu : function(e, t)
8247     {
8248         this.processEvent("contextmenu", e);
8249     },
8250     
8251     processEvent : function(name, e)
8252     {
8253         if (name != 'touchstart' ) {
8254             this.fireEvent(name, e);    
8255         }
8256         
8257         var t = e.getTarget();
8258         
8259         var cell = Roo.get(t);
8260         
8261         if(!cell){
8262             return;
8263         }
8264         
8265         if(cell.findParent('tfoot', false, true)){
8266             return;
8267         }
8268         
8269         if(cell.findParent('thead', false, true)){
8270             
8271             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8272                 cell = Roo.get(t).findParent('th', false, true);
8273                 if (!cell) {
8274                     Roo.log("failed to find th in thead?");
8275                     Roo.log(e.getTarget());
8276                     return;
8277                 }
8278             }
8279             
8280             var cellIndex = cell.dom.cellIndex;
8281             
8282             var ename = name == 'touchstart' ? 'click' : name;
8283             this.fireEvent("header" + ename, this, cellIndex, e);
8284             
8285             return;
8286         }
8287         
8288         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8289             cell = Roo.get(t).findParent('td', false, true);
8290             if (!cell) {
8291                 Roo.log("failed to find th in tbody?");
8292                 Roo.log(e.getTarget());
8293                 return;
8294             }
8295         }
8296         
8297         var row = cell.findParent('tr', false, true);
8298         var cellIndex = cell.dom.cellIndex;
8299         var rowIndex = row.dom.rowIndex - 1;
8300         
8301         if(row !== false){
8302             
8303             this.fireEvent("row" + name, this, rowIndex, e);
8304             
8305             if(cell !== false){
8306             
8307                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8308             }
8309         }
8310         
8311     },
8312     
8313     onMouseover : function(e, el)
8314     {
8315         var cell = Roo.get(el);
8316         
8317         if(!cell){
8318             return;
8319         }
8320         
8321         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8322             cell = cell.findParent('td', false, true);
8323         }
8324         
8325         var row = cell.findParent('tr', false, true);
8326         var cellIndex = cell.dom.cellIndex;
8327         var rowIndex = row.dom.rowIndex - 1; // start from 0
8328         
8329         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8330         
8331     },
8332     
8333     onMouseout : function(e, el)
8334     {
8335         var cell = Roo.get(el);
8336         
8337         if(!cell){
8338             return;
8339         }
8340         
8341         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8342             cell = cell.findParent('td', false, true);
8343         }
8344         
8345         var row = cell.findParent('tr', false, true);
8346         var cellIndex = cell.dom.cellIndex;
8347         var rowIndex = row.dom.rowIndex - 1; // start from 0
8348         
8349         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8350         
8351     },
8352     
8353     onClick : function(e, el)
8354     {
8355         var cell = Roo.get(el);
8356         
8357         if(!cell || (!this.cellSelection && !this.rowSelection)){
8358             return;
8359         }
8360         
8361         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8362             cell = cell.findParent('td', false, true);
8363         }
8364         
8365         if(!cell || typeof(cell) == 'undefined'){
8366             return;
8367         }
8368         
8369         var row = cell.findParent('tr', false, true);
8370         
8371         if(!row || typeof(row) == 'undefined'){
8372             return;
8373         }
8374         
8375         var cellIndex = cell.dom.cellIndex;
8376         var rowIndex = this.getRowIndex(row);
8377         
8378         // why??? - should these not be based on SelectionModel?
8379         if(this.cellSelection){
8380             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8381         }
8382         
8383         if(this.rowSelection){
8384             this.fireEvent('rowclick', this, row, rowIndex, e);
8385         }
8386         
8387         
8388     },
8389         
8390     onDblClick : function(e,el)
8391     {
8392         var cell = Roo.get(el);
8393         
8394         if(!cell || (!this.cellSelection && !this.rowSelection)){
8395             return;
8396         }
8397         
8398         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8399             cell = cell.findParent('td', false, true);
8400         }
8401         
8402         if(!cell || typeof(cell) == 'undefined'){
8403             return;
8404         }
8405         
8406         var row = cell.findParent('tr', false, true);
8407         
8408         if(!row || typeof(row) == 'undefined'){
8409             return;
8410         }
8411         
8412         var cellIndex = cell.dom.cellIndex;
8413         var rowIndex = this.getRowIndex(row);
8414         
8415         if(this.cellSelection){
8416             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8417         }
8418         
8419         if(this.rowSelection){
8420             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8421         }
8422     },
8423     
8424     sort : function(e,el)
8425     {
8426         var col = Roo.get(el);
8427         
8428         if(!col.hasClass('sortable')){
8429             return;
8430         }
8431         
8432         var sort = col.attr('sort');
8433         var dir = 'ASC';
8434         
8435         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8436             dir = 'DESC';
8437         }
8438         
8439         this.store.sortInfo = {field : sort, direction : dir};
8440         
8441         if (this.footer) {
8442             Roo.log("calling footer first");
8443             this.footer.onClick('first');
8444         } else {
8445         
8446             this.store.load({ params : { start : 0 } });
8447         }
8448     },
8449     
8450     renderHeader : function()
8451     {
8452         var header = {
8453             tag: 'thead',
8454             cn : []
8455         };
8456         
8457         var cm = this.cm;
8458         this.totalWidth = 0;
8459         
8460         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8461             
8462             var config = cm.config[i];
8463             
8464             var c = {
8465                 tag: 'th',
8466                 cls : 'x-hcol-' + i,
8467                 style : '',
8468                 html: cm.getColumnHeader(i)
8469             };
8470             
8471             var hh = '';
8472             
8473             if(typeof(config.sortable) != 'undefined' && config.sortable){
8474                 c.cls = 'sortable';
8475                 c.html = '<i class="glyphicon"></i>' + c.html;
8476             }
8477             
8478             // could use BS4 hidden-..-down 
8479             
8480             if(typeof(config.lgHeader) != 'undefined'){
8481                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8482             }
8483             
8484             if(typeof(config.mdHeader) != 'undefined'){
8485                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8486             }
8487             
8488             if(typeof(config.smHeader) != 'undefined'){
8489                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8490             }
8491             
8492             if(typeof(config.xsHeader) != 'undefined'){
8493                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8494             }
8495             
8496             if(hh.length){
8497                 c.html = hh;
8498             }
8499             
8500             if(typeof(config.tooltip) != 'undefined'){
8501                 c.tooltip = config.tooltip;
8502             }
8503             
8504             if(typeof(config.colspan) != 'undefined'){
8505                 c.colspan = config.colspan;
8506             }
8507             
8508             if(typeof(config.hidden) != 'undefined' && config.hidden){
8509                 c.style += ' display:none;';
8510             }
8511             
8512             if(typeof(config.dataIndex) != 'undefined'){
8513                 c.sort = config.dataIndex;
8514             }
8515             
8516            
8517             
8518             if(typeof(config.align) != 'undefined' && config.align.length){
8519                 c.style += ' text-align:' + config.align + ';';
8520             }
8521             
8522             if(typeof(config.width) != 'undefined'){
8523                 c.style += ' width:' + config.width + 'px;';
8524                 this.totalWidth += config.width;
8525             } else {
8526                 this.totalWidth += 100; // assume minimum of 100 per column?
8527             }
8528             
8529             if(typeof(config.cls) != 'undefined'){
8530                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8531             }
8532             
8533             ['xs','sm','md','lg'].map(function(size){
8534                 
8535                 if(typeof(config[size]) == 'undefined'){
8536                     return;
8537                 }
8538                  
8539                 if (!config[size]) { // 0 = hidden
8540                     // BS 4 '0' is treated as hide that column and below.
8541                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8542                     return;
8543                 }
8544                 
8545                 c.cls += ' col-' + size + '-' + config[size] + (
8546                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8547                 );
8548                 
8549                 
8550             });
8551             
8552             header.cn.push(c)
8553         }
8554         
8555         return header;
8556     },
8557     
8558     renderBody : function()
8559     {
8560         var body = {
8561             tag: 'tbody',
8562             cn : [
8563                 {
8564                     tag: 'tr',
8565                     cn : [
8566                         {
8567                             tag : 'td',
8568                             colspan :  this.cm.getColumnCount()
8569                         }
8570                     ]
8571                 }
8572             ]
8573         };
8574         
8575         return body;
8576     },
8577     
8578     renderFooter : function()
8579     {
8580         var footer = {
8581             tag: 'tfoot',
8582             cn : [
8583                 {
8584                     tag: 'tr',
8585                     cn : [
8586                         {
8587                             tag : 'td',
8588                             colspan :  this.cm.getColumnCount()
8589                         }
8590                     ]
8591                 }
8592             ]
8593         };
8594         
8595         return footer;
8596     },
8597     
8598     
8599     
8600     onLoad : function()
8601     {
8602 //        Roo.log('ds onload');
8603         this.clear();
8604         
8605         var _this = this;
8606         var cm = this.cm;
8607         var ds = this.store;
8608         
8609         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8610             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8611             if (_this.store.sortInfo) {
8612                     
8613                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8614                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8615                 }
8616                 
8617                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8618                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8619                 }
8620             }
8621         });
8622         
8623         var tbody =  this.mainBody;
8624               
8625         if(ds.getCount() > 0){
8626             ds.data.each(function(d,rowIndex){
8627                 var row =  this.renderRow(cm, ds, rowIndex);
8628                 
8629                 tbody.createChild(row);
8630                 
8631                 var _this = this;
8632                 
8633                 if(row.cellObjects.length){
8634                     Roo.each(row.cellObjects, function(r){
8635                         _this.renderCellObject(r);
8636                     })
8637                 }
8638                 
8639             }, this);
8640         }
8641         
8642         var tfoot = this.el.select('tfoot', true).first();
8643         
8644         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8645             
8646             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8647             
8648             var total = this.ds.getTotalCount();
8649             
8650             if(this.footer.pageSize < total){
8651                 this.mainFoot.show();
8652             }
8653         }
8654         
8655         Roo.each(this.el.select('tbody td', true).elements, function(e){
8656             e.on('mouseover', _this.onMouseover, _this);
8657         });
8658         
8659         Roo.each(this.el.select('tbody td', true).elements, function(e){
8660             e.on('mouseout', _this.onMouseout, _this);
8661         });
8662         this.fireEvent('rowsrendered', this);
8663         
8664         this.autoSize();
8665     },
8666     
8667     
8668     onUpdate : function(ds,record)
8669     {
8670         this.refreshRow(record);
8671         this.autoSize();
8672     },
8673     
8674     onRemove : function(ds, record, index, isUpdate){
8675         if(isUpdate !== true){
8676             this.fireEvent("beforerowremoved", this, index, record);
8677         }
8678         var bt = this.mainBody.dom;
8679         
8680         var rows = this.el.select('tbody > tr', true).elements;
8681         
8682         if(typeof(rows[index]) != 'undefined'){
8683             bt.removeChild(rows[index].dom);
8684         }
8685         
8686 //        if(bt.rows[index]){
8687 //            bt.removeChild(bt.rows[index]);
8688 //        }
8689         
8690         if(isUpdate !== true){
8691             //this.stripeRows(index);
8692             //this.syncRowHeights(index, index);
8693             //this.layout();
8694             this.fireEvent("rowremoved", this, index, record);
8695         }
8696     },
8697     
8698     onAdd : function(ds, records, rowIndex)
8699     {
8700         //Roo.log('on Add called');
8701         // - note this does not handle multiple adding very well..
8702         var bt = this.mainBody.dom;
8703         for (var i =0 ; i < records.length;i++) {
8704             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8705             //Roo.log(records[i]);
8706             //Roo.log(this.store.getAt(rowIndex+i));
8707             this.insertRow(this.store, rowIndex + i, false);
8708             return;
8709         }
8710         
8711     },
8712     
8713     
8714     refreshRow : function(record){
8715         var ds = this.store, index;
8716         if(typeof record == 'number'){
8717             index = record;
8718             record = ds.getAt(index);
8719         }else{
8720             index = ds.indexOf(record);
8721             if (index < 0) {
8722                 return; // should not happen - but seems to 
8723             }
8724         }
8725         this.insertRow(ds, index, true);
8726         this.autoSize();
8727         this.onRemove(ds, record, index+1, true);
8728         this.autoSize();
8729         //this.syncRowHeights(index, index);
8730         //this.layout();
8731         this.fireEvent("rowupdated", this, index, record);
8732     },
8733     
8734     insertRow : function(dm, rowIndex, isUpdate){
8735         
8736         if(!isUpdate){
8737             this.fireEvent("beforerowsinserted", this, rowIndex);
8738         }
8739             //var s = this.getScrollState();
8740         var row = this.renderRow(this.cm, this.store, rowIndex);
8741         // insert before rowIndex..
8742         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8743         
8744         var _this = this;
8745                 
8746         if(row.cellObjects.length){
8747             Roo.each(row.cellObjects, function(r){
8748                 _this.renderCellObject(r);
8749             })
8750         }
8751             
8752         if(!isUpdate){
8753             this.fireEvent("rowsinserted", this, rowIndex);
8754             //this.syncRowHeights(firstRow, lastRow);
8755             //this.stripeRows(firstRow);
8756             //this.layout();
8757         }
8758         
8759     },
8760     
8761     
8762     getRowDom : function(rowIndex)
8763     {
8764         var rows = this.el.select('tbody > tr', true).elements;
8765         
8766         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8767         
8768     },
8769     // returns the object tree for a tr..
8770   
8771     
8772     renderRow : function(cm, ds, rowIndex) 
8773     {
8774         var d = ds.getAt(rowIndex);
8775         
8776         var row = {
8777             tag : 'tr',
8778             cls : 'x-row-' + rowIndex,
8779             cn : []
8780         };
8781             
8782         var cellObjects = [];
8783         
8784         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8785             var config = cm.config[i];
8786             
8787             var renderer = cm.getRenderer(i);
8788             var value = '';
8789             var id = false;
8790             
8791             if(typeof(renderer) !== 'undefined'){
8792                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8793             }
8794             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8795             // and are rendered into the cells after the row is rendered - using the id for the element.
8796             
8797             if(typeof(value) === 'object'){
8798                 id = Roo.id();
8799                 cellObjects.push({
8800                     container : id,
8801                     cfg : value 
8802                 })
8803             }
8804             
8805             var rowcfg = {
8806                 record: d,
8807                 rowIndex : rowIndex,
8808                 colIndex : i,
8809                 rowClass : ''
8810             };
8811
8812             this.fireEvent('rowclass', this, rowcfg);
8813             
8814             var td = {
8815                 tag: 'td',
8816                 cls : rowcfg.rowClass + ' x-col-' + i,
8817                 style: '',
8818                 html: (typeof(value) === 'object') ? '' : value
8819             };
8820             
8821             if (id) {
8822                 td.id = id;
8823             }
8824             
8825             if(typeof(config.colspan) != 'undefined'){
8826                 td.colspan = config.colspan;
8827             }
8828             
8829             if(typeof(config.hidden) != 'undefined' && config.hidden){
8830                 td.style += ' display:none;';
8831             }
8832             
8833             if(typeof(config.align) != 'undefined' && config.align.length){
8834                 td.style += ' text-align:' + config.align + ';';
8835             }
8836             if(typeof(config.valign) != 'undefined' && config.valign.length){
8837                 td.style += ' vertical-align:' + config.valign + ';';
8838             }
8839             
8840             if(typeof(config.width) != 'undefined'){
8841                 td.style += ' width:' +  config.width + 'px;';
8842             }
8843             
8844             if(typeof(config.cursor) != 'undefined'){
8845                 td.style += ' cursor:' +  config.cursor + ';';
8846             }
8847             
8848             if(typeof(config.cls) != 'undefined'){
8849                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8850             }
8851             
8852             ['xs','sm','md','lg'].map(function(size){
8853                 
8854                 if(typeof(config[size]) == 'undefined'){
8855                     return;
8856                 }
8857                 
8858                 
8859                   
8860                 if (!config[size]) { // 0 = hidden
8861                     // BS 4 '0' is treated as hide that column and below.
8862                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8863                     return;
8864                 }
8865                 
8866                 td.cls += ' col-' + size + '-' + config[size] + (
8867                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8868                 );
8869                  
8870
8871             });
8872             
8873             row.cn.push(td);
8874            
8875         }
8876         
8877         row.cellObjects = cellObjects;
8878         
8879         return row;
8880           
8881     },
8882     
8883     
8884     
8885     onBeforeLoad : function()
8886     {
8887         
8888     },
8889      /**
8890      * Remove all rows
8891      */
8892     clear : function()
8893     {
8894         this.el.select('tbody', true).first().dom.innerHTML = '';
8895     },
8896     /**
8897      * Show or hide a row.
8898      * @param {Number} rowIndex to show or hide
8899      * @param {Boolean} state hide
8900      */
8901     setRowVisibility : function(rowIndex, state)
8902     {
8903         var bt = this.mainBody.dom;
8904         
8905         var rows = this.el.select('tbody > tr', true).elements;
8906         
8907         if(typeof(rows[rowIndex]) == 'undefined'){
8908             return;
8909         }
8910         rows[rowIndex].dom.style.display = state ? '' : 'none';
8911     },
8912     
8913     
8914     getSelectionModel : function(){
8915         if(!this.selModel){
8916             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8917         }
8918         return this.selModel;
8919     },
8920     /*
8921      * Render the Roo.bootstrap object from renderder
8922      */
8923     renderCellObject : function(r)
8924     {
8925         var _this = this;
8926         
8927         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8928         
8929         var t = r.cfg.render(r.container);
8930         
8931         if(r.cfg.cn){
8932             Roo.each(r.cfg.cn, function(c){
8933                 var child = {
8934                     container: t.getChildContainer(),
8935                     cfg: c
8936                 };
8937                 _this.renderCellObject(child);
8938             })
8939         }
8940     },
8941     
8942     getRowIndex : function(row)
8943     {
8944         var rowIndex = -1;
8945         
8946         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8947             if(el != row){
8948                 return;
8949             }
8950             
8951             rowIndex = index;
8952         });
8953         
8954         return rowIndex;
8955     },
8956      /**
8957      * Returns the grid's underlying element = used by panel.Grid
8958      * @return {Element} The element
8959      */
8960     getGridEl : function(){
8961         return this.el;
8962     },
8963      /**
8964      * Forces a resize - used by panel.Grid
8965      * @return {Element} The element
8966      */
8967     autoSize : function()
8968     {
8969         //var ctr = Roo.get(this.container.dom.parentElement);
8970         var ctr = Roo.get(this.el.dom);
8971         
8972         var thd = this.getGridEl().select('thead',true).first();
8973         var tbd = this.getGridEl().select('tbody', true).first();
8974         var tfd = this.getGridEl().select('tfoot', true).first();
8975         
8976         var cw = ctr.getWidth();
8977         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8978         
8979         if (tbd) {
8980             
8981             tbd.setWidth(ctr.getWidth());
8982             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8983             // this needs fixing for various usage - currently only hydra job advers I think..
8984             //tdb.setHeight(
8985             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8986             //); 
8987             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8988             cw -= barsize;
8989         }
8990         cw = Math.max(cw, this.totalWidth);
8991         this.getGridEl().select('tbody tr',true).setWidth(cw);
8992         
8993         // resize 'expandable coloumn?
8994         
8995         return; // we doe not have a view in this design..
8996         
8997     },
8998     onBodyScroll: function()
8999     {
9000         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9001         if(this.mainHead){
9002             this.mainHead.setStyle({
9003                 'position' : 'relative',
9004                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9005             });
9006         }
9007         
9008         if(this.lazyLoad){
9009             
9010             var scrollHeight = this.mainBody.dom.scrollHeight;
9011             
9012             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9013             
9014             var height = this.mainBody.getHeight();
9015             
9016             if(scrollHeight - height == scrollTop) {
9017                 
9018                 var total = this.ds.getTotalCount();
9019                 
9020                 if(this.footer.cursor + this.footer.pageSize < total){
9021                     
9022                     this.footer.ds.load({
9023                         params : {
9024                             start : this.footer.cursor + this.footer.pageSize,
9025                             limit : this.footer.pageSize
9026                         },
9027                         add : true
9028                     });
9029                 }
9030             }
9031             
9032         }
9033     },
9034     
9035     onHeaderChange : function()
9036     {
9037         var header = this.renderHeader();
9038         var table = this.el.select('table', true).first();
9039         
9040         this.mainHead.remove();
9041         this.mainHead = table.createChild(header, this.mainBody, false);
9042     },
9043     
9044     onHiddenChange : function(colModel, colIndex, hidden)
9045     {
9046         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9047         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9048         
9049         this.CSS.updateRule(thSelector, "display", "");
9050         this.CSS.updateRule(tdSelector, "display", "");
9051         
9052         if(hidden){
9053             this.CSS.updateRule(thSelector, "display", "none");
9054             this.CSS.updateRule(tdSelector, "display", "none");
9055         }
9056         
9057         this.onHeaderChange();
9058         this.onLoad();
9059     },
9060     
9061     setColumnWidth: function(col_index, width)
9062     {
9063         // width = "md-2 xs-2..."
9064         if(!this.colModel.config[col_index]) {
9065             return;
9066         }
9067         
9068         var w = width.split(" ");
9069         
9070         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9071         
9072         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9073         
9074         
9075         for(var j = 0; j < w.length; j++) {
9076             
9077             if(!w[j]) {
9078                 continue;
9079             }
9080             
9081             var size_cls = w[j].split("-");
9082             
9083             if(!Number.isInteger(size_cls[1] * 1)) {
9084                 continue;
9085             }
9086             
9087             if(!this.colModel.config[col_index][size_cls[0]]) {
9088                 continue;
9089             }
9090             
9091             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9092                 continue;
9093             }
9094             
9095             h_row[0].classList.replace(
9096                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9097                 "col-"+size_cls[0]+"-"+size_cls[1]
9098             );
9099             
9100             for(var i = 0; i < rows.length; i++) {
9101                 
9102                 var size_cls = w[j].split("-");
9103                 
9104                 if(!Number.isInteger(size_cls[1] * 1)) {
9105                     continue;
9106                 }
9107                 
9108                 if(!this.colModel.config[col_index][size_cls[0]]) {
9109                     continue;
9110                 }
9111                 
9112                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9113                     continue;
9114                 }
9115                 
9116                 rows[i].classList.replace(
9117                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9118                     "col-"+size_cls[0]+"-"+size_cls[1]
9119                 );
9120             }
9121             
9122             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9123         }
9124     }
9125 });
9126
9127  
9128
9129  /*
9130  * - LGPL
9131  *
9132  * table cell
9133  * 
9134  */
9135
9136 /**
9137  * @class Roo.bootstrap.TableCell
9138  * @extends Roo.bootstrap.Component
9139  * Bootstrap TableCell class
9140  * @cfg {String} html cell contain text
9141  * @cfg {String} cls cell class
9142  * @cfg {String} tag cell tag (td|th) default td
9143  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9144  * @cfg {String} align Aligns the content in a cell
9145  * @cfg {String} axis Categorizes cells
9146  * @cfg {String} bgcolor Specifies the background color of a cell
9147  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9148  * @cfg {Number} colspan Specifies the number of columns a cell should span
9149  * @cfg {String} headers Specifies one or more header cells a cell is related to
9150  * @cfg {Number} height Sets the height of a cell
9151  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9152  * @cfg {Number} rowspan Sets the number of rows a cell should span
9153  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9154  * @cfg {String} valign Vertical aligns the content in a cell
9155  * @cfg {Number} width Specifies the width of a cell
9156  * 
9157  * @constructor
9158  * Create a new TableCell
9159  * @param {Object} config The config object
9160  */
9161
9162 Roo.bootstrap.TableCell = function(config){
9163     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9164 };
9165
9166 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9167     
9168     html: false,
9169     cls: false,
9170     tag: false,
9171     abbr: false,
9172     align: false,
9173     axis: false,
9174     bgcolor: false,
9175     charoff: false,
9176     colspan: false,
9177     headers: false,
9178     height: false,
9179     nowrap: false,
9180     rowspan: false,
9181     scope: false,
9182     valign: false,
9183     width: false,
9184     
9185     
9186     getAutoCreate : function(){
9187         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9188         
9189         cfg = {
9190             tag: 'td'
9191         };
9192         
9193         if(this.tag){
9194             cfg.tag = this.tag;
9195         }
9196         
9197         if (this.html) {
9198             cfg.html=this.html
9199         }
9200         if (this.cls) {
9201             cfg.cls=this.cls
9202         }
9203         if (this.abbr) {
9204             cfg.abbr=this.abbr
9205         }
9206         if (this.align) {
9207             cfg.align=this.align
9208         }
9209         if (this.axis) {
9210             cfg.axis=this.axis
9211         }
9212         if (this.bgcolor) {
9213             cfg.bgcolor=this.bgcolor
9214         }
9215         if (this.charoff) {
9216             cfg.charoff=this.charoff
9217         }
9218         if (this.colspan) {
9219             cfg.colspan=this.colspan
9220         }
9221         if (this.headers) {
9222             cfg.headers=this.headers
9223         }
9224         if (this.height) {
9225             cfg.height=this.height
9226         }
9227         if (this.nowrap) {
9228             cfg.nowrap=this.nowrap
9229         }
9230         if (this.rowspan) {
9231             cfg.rowspan=this.rowspan
9232         }
9233         if (this.scope) {
9234             cfg.scope=this.scope
9235         }
9236         if (this.valign) {
9237             cfg.valign=this.valign
9238         }
9239         if (this.width) {
9240             cfg.width=this.width
9241         }
9242         
9243         
9244         return cfg;
9245     }
9246    
9247 });
9248
9249  
9250
9251  /*
9252  * - LGPL
9253  *
9254  * table row
9255  * 
9256  */
9257
9258 /**
9259  * @class Roo.bootstrap.TableRow
9260  * @extends Roo.bootstrap.Component
9261  * Bootstrap TableRow class
9262  * @cfg {String} cls row class
9263  * @cfg {String} align Aligns the content in a table row
9264  * @cfg {String} bgcolor Specifies a background color for a table row
9265  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9266  * @cfg {String} valign Vertical aligns the content in a table row
9267  * 
9268  * @constructor
9269  * Create a new TableRow
9270  * @param {Object} config The config object
9271  */
9272
9273 Roo.bootstrap.TableRow = function(config){
9274     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9275 };
9276
9277 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9278     
9279     cls: false,
9280     align: false,
9281     bgcolor: false,
9282     charoff: false,
9283     valign: false,
9284     
9285     getAutoCreate : function(){
9286         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9287         
9288         cfg = {
9289             tag: 'tr'
9290         };
9291             
9292         if(this.cls){
9293             cfg.cls = this.cls;
9294         }
9295         if(this.align){
9296             cfg.align = this.align;
9297         }
9298         if(this.bgcolor){
9299             cfg.bgcolor = this.bgcolor;
9300         }
9301         if(this.charoff){
9302             cfg.charoff = this.charoff;
9303         }
9304         if(this.valign){
9305             cfg.valign = this.valign;
9306         }
9307         
9308         return cfg;
9309     }
9310    
9311 });
9312
9313  
9314
9315  /*
9316  * - LGPL
9317  *
9318  * table body
9319  * 
9320  */
9321
9322 /**
9323  * @class Roo.bootstrap.TableBody
9324  * @extends Roo.bootstrap.Component
9325  * Bootstrap TableBody class
9326  * @cfg {String} cls element class
9327  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9328  * @cfg {String} align Aligns the content inside the element
9329  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9330  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9331  * 
9332  * @constructor
9333  * Create a new TableBody
9334  * @param {Object} config The config object
9335  */
9336
9337 Roo.bootstrap.TableBody = function(config){
9338     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9339 };
9340
9341 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9342     
9343     cls: false,
9344     tag: false,
9345     align: false,
9346     charoff: false,
9347     valign: false,
9348     
9349     getAutoCreate : function(){
9350         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9351         
9352         cfg = {
9353             tag: 'tbody'
9354         };
9355             
9356         if (this.cls) {
9357             cfg.cls=this.cls
9358         }
9359         if(this.tag){
9360             cfg.tag = this.tag;
9361         }
9362         
9363         if(this.align){
9364             cfg.align = this.align;
9365         }
9366         if(this.charoff){
9367             cfg.charoff = this.charoff;
9368         }
9369         if(this.valign){
9370             cfg.valign = this.valign;
9371         }
9372         
9373         return cfg;
9374     }
9375     
9376     
9377 //    initEvents : function()
9378 //    {
9379 //        
9380 //        if(!this.store){
9381 //            return;
9382 //        }
9383 //        
9384 //        this.store = Roo.factory(this.store, Roo.data);
9385 //        this.store.on('load', this.onLoad, this);
9386 //        
9387 //        this.store.load();
9388 //        
9389 //    },
9390 //    
9391 //    onLoad: function () 
9392 //    {   
9393 //        this.fireEvent('load', this);
9394 //    }
9395 //    
9396 //   
9397 });
9398
9399  
9400
9401  /*
9402  * Based on:
9403  * Ext JS Library 1.1.1
9404  * Copyright(c) 2006-2007, Ext JS, LLC.
9405  *
9406  * Originally Released Under LGPL - original licence link has changed is not relivant.
9407  *
9408  * Fork - LGPL
9409  * <script type="text/javascript">
9410  */
9411
9412 // as we use this in bootstrap.
9413 Roo.namespace('Roo.form');
9414  /**
9415  * @class Roo.form.Action
9416  * Internal Class used to handle form actions
9417  * @constructor
9418  * @param {Roo.form.BasicForm} el The form element or its id
9419  * @param {Object} config Configuration options
9420  */
9421
9422  
9423  
9424 // define the action interface
9425 Roo.form.Action = function(form, options){
9426     this.form = form;
9427     this.options = options || {};
9428 };
9429 /**
9430  * Client Validation Failed
9431  * @const 
9432  */
9433 Roo.form.Action.CLIENT_INVALID = 'client';
9434 /**
9435  * Server Validation Failed
9436  * @const 
9437  */
9438 Roo.form.Action.SERVER_INVALID = 'server';
9439  /**
9440  * Connect to Server Failed
9441  * @const 
9442  */
9443 Roo.form.Action.CONNECT_FAILURE = 'connect';
9444 /**
9445  * Reading Data from Server Failed
9446  * @const 
9447  */
9448 Roo.form.Action.LOAD_FAILURE = 'load';
9449
9450 Roo.form.Action.prototype = {
9451     type : 'default',
9452     failureType : undefined,
9453     response : undefined,
9454     result : undefined,
9455
9456     // interface method
9457     run : function(options){
9458
9459     },
9460
9461     // interface method
9462     success : function(response){
9463
9464     },
9465
9466     // interface method
9467     handleResponse : function(response){
9468
9469     },
9470
9471     // default connection failure
9472     failure : function(response){
9473         
9474         this.response = response;
9475         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9476         this.form.afterAction(this, false);
9477     },
9478
9479     processResponse : function(response){
9480         this.response = response;
9481         if(!response.responseText){
9482             return true;
9483         }
9484         this.result = this.handleResponse(response);
9485         return this.result;
9486     },
9487
9488     // utility functions used internally
9489     getUrl : function(appendParams){
9490         var url = this.options.url || this.form.url || this.form.el.dom.action;
9491         if(appendParams){
9492             var p = this.getParams();
9493             if(p){
9494                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9495             }
9496         }
9497         return url;
9498     },
9499
9500     getMethod : function(){
9501         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9502     },
9503
9504     getParams : function(){
9505         var bp = this.form.baseParams;
9506         var p = this.options.params;
9507         if(p){
9508             if(typeof p == "object"){
9509                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9510             }else if(typeof p == 'string' && bp){
9511                 p += '&' + Roo.urlEncode(bp);
9512             }
9513         }else if(bp){
9514             p = Roo.urlEncode(bp);
9515         }
9516         return p;
9517     },
9518
9519     createCallback : function(){
9520         return {
9521             success: this.success,
9522             failure: this.failure,
9523             scope: this,
9524             timeout: (this.form.timeout*1000),
9525             upload: this.form.fileUpload ? this.success : undefined
9526         };
9527     }
9528 };
9529
9530 Roo.form.Action.Submit = function(form, options){
9531     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9532 };
9533
9534 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9535     type : 'submit',
9536
9537     haveProgress : false,
9538     uploadComplete : false,
9539     
9540     // uploadProgress indicator.
9541     uploadProgress : function()
9542     {
9543         if (!this.form.progressUrl) {
9544             return;
9545         }
9546         
9547         if (!this.haveProgress) {
9548             Roo.MessageBox.progress("Uploading", "Uploading");
9549         }
9550         if (this.uploadComplete) {
9551            Roo.MessageBox.hide();
9552            return;
9553         }
9554         
9555         this.haveProgress = true;
9556    
9557         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9558         
9559         var c = new Roo.data.Connection();
9560         c.request({
9561             url : this.form.progressUrl,
9562             params: {
9563                 id : uid
9564             },
9565             method: 'GET',
9566             success : function(req){
9567                //console.log(data);
9568                 var rdata = false;
9569                 var edata;
9570                 try  {
9571                    rdata = Roo.decode(req.responseText)
9572                 } catch (e) {
9573                     Roo.log("Invalid data from server..");
9574                     Roo.log(edata);
9575                     return;
9576                 }
9577                 if (!rdata || !rdata.success) {
9578                     Roo.log(rdata);
9579                     Roo.MessageBox.alert(Roo.encode(rdata));
9580                     return;
9581                 }
9582                 var data = rdata.data;
9583                 
9584                 if (this.uploadComplete) {
9585                    Roo.MessageBox.hide();
9586                    return;
9587                 }
9588                    
9589                 if (data){
9590                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9591                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9592                     );
9593                 }
9594                 this.uploadProgress.defer(2000,this);
9595             },
9596        
9597             failure: function(data) {
9598                 Roo.log('progress url failed ');
9599                 Roo.log(data);
9600             },
9601             scope : this
9602         });
9603            
9604     },
9605     
9606     
9607     run : function()
9608     {
9609         // run get Values on the form, so it syncs any secondary forms.
9610         this.form.getValues();
9611         
9612         var o = this.options;
9613         var method = this.getMethod();
9614         var isPost = method == 'POST';
9615         if(o.clientValidation === false || this.form.isValid()){
9616             
9617             if (this.form.progressUrl) {
9618                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9619                     (new Date() * 1) + '' + Math.random());
9620                     
9621             } 
9622             
9623             
9624             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9625                 form:this.form.el.dom,
9626                 url:this.getUrl(!isPost),
9627                 method: method,
9628                 params:isPost ? this.getParams() : null,
9629                 isUpload: this.form.fileUpload,
9630                 formData : this.form.formData
9631             }));
9632             
9633             this.uploadProgress();
9634
9635         }else if (o.clientValidation !== false){ // client validation failed
9636             this.failureType = Roo.form.Action.CLIENT_INVALID;
9637             this.form.afterAction(this, false);
9638         }
9639     },
9640
9641     success : function(response)
9642     {
9643         this.uploadComplete= true;
9644         if (this.haveProgress) {
9645             Roo.MessageBox.hide();
9646         }
9647         
9648         
9649         var result = this.processResponse(response);
9650         if(result === true || result.success){
9651             this.form.afterAction(this, true);
9652             return;
9653         }
9654         if(result.errors){
9655             this.form.markInvalid(result.errors);
9656             this.failureType = Roo.form.Action.SERVER_INVALID;
9657         }
9658         this.form.afterAction(this, false);
9659     },
9660     failure : function(response)
9661     {
9662         this.uploadComplete= true;
9663         if (this.haveProgress) {
9664             Roo.MessageBox.hide();
9665         }
9666         
9667         this.response = response;
9668         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9669         this.form.afterAction(this, false);
9670     },
9671     
9672     handleResponse : function(response){
9673         if(this.form.errorReader){
9674             var rs = this.form.errorReader.read(response);
9675             var errors = [];
9676             if(rs.records){
9677                 for(var i = 0, len = rs.records.length; i < len; i++) {
9678                     var r = rs.records[i];
9679                     errors[i] = r.data;
9680                 }
9681             }
9682             if(errors.length < 1){
9683                 errors = null;
9684             }
9685             return {
9686                 success : rs.success,
9687                 errors : errors
9688             };
9689         }
9690         var ret = false;
9691         try {
9692             ret = Roo.decode(response.responseText);
9693         } catch (e) {
9694             ret = {
9695                 success: false,
9696                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9697                 errors : []
9698             };
9699         }
9700         return ret;
9701         
9702     }
9703 });
9704
9705
9706 Roo.form.Action.Load = function(form, options){
9707     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9708     this.reader = this.form.reader;
9709 };
9710
9711 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9712     type : 'load',
9713
9714     run : function(){
9715         
9716         Roo.Ajax.request(Roo.apply(
9717                 this.createCallback(), {
9718                     method:this.getMethod(),
9719                     url:this.getUrl(false),
9720                     params:this.getParams()
9721         }));
9722     },
9723
9724     success : function(response){
9725         
9726         var result = this.processResponse(response);
9727         if(result === true || !result.success || !result.data){
9728             this.failureType = Roo.form.Action.LOAD_FAILURE;
9729             this.form.afterAction(this, false);
9730             return;
9731         }
9732         this.form.clearInvalid();
9733         this.form.setValues(result.data);
9734         this.form.afterAction(this, true);
9735     },
9736
9737     handleResponse : function(response){
9738         if(this.form.reader){
9739             var rs = this.form.reader.read(response);
9740             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9741             return {
9742                 success : rs.success,
9743                 data : data
9744             };
9745         }
9746         return Roo.decode(response.responseText);
9747     }
9748 });
9749
9750 Roo.form.Action.ACTION_TYPES = {
9751     'load' : Roo.form.Action.Load,
9752     'submit' : Roo.form.Action.Submit
9753 };/*
9754  * - LGPL
9755  *
9756  * form
9757  *
9758  */
9759
9760 /**
9761  * @class Roo.bootstrap.Form
9762  * @extends Roo.bootstrap.Component
9763  * Bootstrap Form class
9764  * @cfg {String} method  GET | POST (default POST)
9765  * @cfg {String} labelAlign top | left (default top)
9766  * @cfg {String} align left  | right - for navbars
9767  * @cfg {Boolean} loadMask load mask when submit (default true)
9768
9769  *
9770  * @constructor
9771  * Create a new Form
9772  * @param {Object} config The config object
9773  */
9774
9775
9776 Roo.bootstrap.Form = function(config){
9777     
9778     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9779     
9780     Roo.bootstrap.Form.popover.apply();
9781     
9782     this.addEvents({
9783         /**
9784          * @event clientvalidation
9785          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9786          * @param {Form} this
9787          * @param {Boolean} valid true if the form has passed client-side validation
9788          */
9789         clientvalidation: true,
9790         /**
9791          * @event beforeaction
9792          * Fires before any action is performed. Return false to cancel the action.
9793          * @param {Form} this
9794          * @param {Action} action The action to be performed
9795          */
9796         beforeaction: true,
9797         /**
9798          * @event actionfailed
9799          * Fires when an action fails.
9800          * @param {Form} this
9801          * @param {Action} action The action that failed
9802          */
9803         actionfailed : true,
9804         /**
9805          * @event actioncomplete
9806          * Fires when an action is completed.
9807          * @param {Form} this
9808          * @param {Action} action The action that completed
9809          */
9810         actioncomplete : true
9811     });
9812 };
9813
9814 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9815
9816      /**
9817      * @cfg {String} method
9818      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9819      */
9820     method : 'POST',
9821     /**
9822      * @cfg {String} url
9823      * The URL to use for form actions if one isn't supplied in the action options.
9824      */
9825     /**
9826      * @cfg {Boolean} fileUpload
9827      * Set to true if this form is a file upload.
9828      */
9829
9830     /**
9831      * @cfg {Object} baseParams
9832      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9833      */
9834
9835     /**
9836      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9837      */
9838     timeout: 30,
9839     /**
9840      * @cfg {Sting} align (left|right) for navbar forms
9841      */
9842     align : 'left',
9843
9844     // private
9845     activeAction : null,
9846
9847     /**
9848      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9849      * element by passing it or its id or mask the form itself by passing in true.
9850      * @type Mixed
9851      */
9852     waitMsgTarget : false,
9853
9854     loadMask : true,
9855     
9856     /**
9857      * @cfg {Boolean} errorMask (true|false) default false
9858      */
9859     errorMask : false,
9860     
9861     /**
9862      * @cfg {Number} maskOffset Default 100
9863      */
9864     maskOffset : 100,
9865     
9866     /**
9867      * @cfg {Boolean} maskBody
9868      */
9869     maskBody : false,
9870
9871     getAutoCreate : function(){
9872
9873         var cfg = {
9874             tag: 'form',
9875             method : this.method || 'POST',
9876             id : this.id || Roo.id(),
9877             cls : ''
9878         };
9879         if (this.parent().xtype.match(/^Nav/)) {
9880             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9881
9882         }
9883
9884         if (this.labelAlign == 'left' ) {
9885             cfg.cls += ' form-horizontal';
9886         }
9887
9888
9889         return cfg;
9890     },
9891     initEvents : function()
9892     {
9893         this.el.on('submit', this.onSubmit, this);
9894         // this was added as random key presses on the form where triggering form submit.
9895         this.el.on('keypress', function(e) {
9896             if (e.getCharCode() != 13) {
9897                 return true;
9898             }
9899             // we might need to allow it for textareas.. and some other items.
9900             // check e.getTarget().
9901
9902             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9903                 return true;
9904             }
9905
9906             Roo.log("keypress blocked");
9907
9908             e.preventDefault();
9909             return false;
9910         });
9911         
9912     },
9913     // private
9914     onSubmit : function(e){
9915         e.stopEvent();
9916     },
9917
9918      /**
9919      * Returns true if client-side validation on the form is successful.
9920      * @return Boolean
9921      */
9922     isValid : function(){
9923         var items = this.getItems();
9924         var valid = true;
9925         var target = false;
9926         
9927         items.each(function(f){
9928             
9929             if(f.validate()){
9930                 return;
9931             }
9932             
9933             Roo.log('invalid field: ' + f.name);
9934             
9935             valid = false;
9936
9937             if(!target && f.el.isVisible(true)){
9938                 target = f;
9939             }
9940            
9941         });
9942         
9943         if(this.errorMask && !valid){
9944             Roo.bootstrap.Form.popover.mask(this, target);
9945         }
9946         
9947         return valid;
9948     },
9949     
9950     /**
9951      * Returns true if any fields in this form have changed since their original load.
9952      * @return Boolean
9953      */
9954     isDirty : function(){
9955         var dirty = false;
9956         var items = this.getItems();
9957         items.each(function(f){
9958            if(f.isDirty()){
9959                dirty = true;
9960                return false;
9961            }
9962            return true;
9963         });
9964         return dirty;
9965     },
9966      /**
9967      * Performs a predefined action (submit or load) or custom actions you define on this form.
9968      * @param {String} actionName The name of the action type
9969      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9970      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9971      * accept other config options):
9972      * <pre>
9973 Property          Type             Description
9974 ----------------  ---------------  ----------------------------------------------------------------------------------
9975 url               String           The url for the action (defaults to the form's url)
9976 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9977 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9978 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9979                                    validate the form on the client (defaults to false)
9980      * </pre>
9981      * @return {BasicForm} this
9982      */
9983     doAction : function(action, options){
9984         if(typeof action == 'string'){
9985             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9986         }
9987         if(this.fireEvent('beforeaction', this, action) !== false){
9988             this.beforeAction(action);
9989             action.run.defer(100, action);
9990         }
9991         return this;
9992     },
9993
9994     // private
9995     beforeAction : function(action){
9996         var o = action.options;
9997         
9998         if(this.loadMask){
9999             
10000             if(this.maskBody){
10001                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10002             } else {
10003                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10004             }
10005         }
10006         // not really supported yet.. ??
10007
10008         //if(this.waitMsgTarget === true){
10009         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10010         //}else if(this.waitMsgTarget){
10011         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10012         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10013         //}else {
10014         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10015        // }
10016
10017     },
10018
10019     // private
10020     afterAction : function(action, success){
10021         this.activeAction = null;
10022         var o = action.options;
10023
10024         if(this.loadMask){
10025             
10026             if(this.maskBody){
10027                 Roo.get(document.body).unmask();
10028             } else {
10029                 this.el.unmask();
10030             }
10031         }
10032         
10033         //if(this.waitMsgTarget === true){
10034 //            this.el.unmask();
10035         //}else if(this.waitMsgTarget){
10036         //    this.waitMsgTarget.unmask();
10037         //}else{
10038         //    Roo.MessageBox.updateProgress(1);
10039         //    Roo.MessageBox.hide();
10040        // }
10041         //
10042         if(success){
10043             if(o.reset){
10044                 this.reset();
10045             }
10046             Roo.callback(o.success, o.scope, [this, action]);
10047             this.fireEvent('actioncomplete', this, action);
10048
10049         }else{
10050
10051             // failure condition..
10052             // we have a scenario where updates need confirming.
10053             // eg. if a locking scenario exists..
10054             // we look for { errors : { needs_confirm : true }} in the response.
10055             if (
10056                 (typeof(action.result) != 'undefined')  &&
10057                 (typeof(action.result.errors) != 'undefined')  &&
10058                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10059            ){
10060                 var _t = this;
10061                 Roo.log("not supported yet");
10062                  /*
10063
10064                 Roo.MessageBox.confirm(
10065                     "Change requires confirmation",
10066                     action.result.errorMsg,
10067                     function(r) {
10068                         if (r != 'yes') {
10069                             return;
10070                         }
10071                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10072                     }
10073
10074                 );
10075                 */
10076
10077
10078                 return;
10079             }
10080
10081             Roo.callback(o.failure, o.scope, [this, action]);
10082             // show an error message if no failed handler is set..
10083             if (!this.hasListener('actionfailed')) {
10084                 Roo.log("need to add dialog support");
10085                 /*
10086                 Roo.MessageBox.alert("Error",
10087                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10088                         action.result.errorMsg :
10089                         "Saving Failed, please check your entries or try again"
10090                 );
10091                 */
10092             }
10093
10094             this.fireEvent('actionfailed', this, action);
10095         }
10096
10097     },
10098     /**
10099      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10100      * @param {String} id The value to search for
10101      * @return Field
10102      */
10103     findField : function(id){
10104         var items = this.getItems();
10105         var field = items.get(id);
10106         if(!field){
10107              items.each(function(f){
10108                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10109                     field = f;
10110                     return false;
10111                 }
10112                 return true;
10113             });
10114         }
10115         return field || null;
10116     },
10117      /**
10118      * Mark fields in this form invalid in bulk.
10119      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10120      * @return {BasicForm} this
10121      */
10122     markInvalid : function(errors){
10123         if(errors instanceof Array){
10124             for(var i = 0, len = errors.length; i < len; i++){
10125                 var fieldError = errors[i];
10126                 var f = this.findField(fieldError.id);
10127                 if(f){
10128                     f.markInvalid(fieldError.msg);
10129                 }
10130             }
10131         }else{
10132             var field, id;
10133             for(id in errors){
10134                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10135                     field.markInvalid(errors[id]);
10136                 }
10137             }
10138         }
10139         //Roo.each(this.childForms || [], function (f) {
10140         //    f.markInvalid(errors);
10141         //});
10142
10143         return this;
10144     },
10145
10146     /**
10147      * Set values for fields in this form in bulk.
10148      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10149      * @return {BasicForm} this
10150      */
10151     setValues : function(values){
10152         if(values instanceof Array){ // array of objects
10153             for(var i = 0, len = values.length; i < len; i++){
10154                 var v = values[i];
10155                 var f = this.findField(v.id);
10156                 if(f){
10157                     f.setValue(v.value);
10158                     if(this.trackResetOnLoad){
10159                         f.originalValue = f.getValue();
10160                     }
10161                 }
10162             }
10163         }else{ // object hash
10164             var field, id;
10165             for(id in values){
10166                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10167
10168                     if (field.setFromData &&
10169                         field.valueField &&
10170                         field.displayField &&
10171                         // combos' with local stores can
10172                         // be queried via setValue()
10173                         // to set their value..
10174                         (field.store && !field.store.isLocal)
10175                         ) {
10176                         // it's a combo
10177                         var sd = { };
10178                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10179                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10180                         field.setFromData(sd);
10181
10182                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10183                         
10184                         field.setFromData(values);
10185                         
10186                     } else {
10187                         field.setValue(values[id]);
10188                     }
10189
10190
10191                     if(this.trackResetOnLoad){
10192                         field.originalValue = field.getValue();
10193                     }
10194                 }
10195             }
10196         }
10197
10198         //Roo.each(this.childForms || [], function (f) {
10199         //    f.setValues(values);
10200         //});
10201
10202         return this;
10203     },
10204
10205     /**
10206      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10207      * they are returned as an array.
10208      * @param {Boolean} asString
10209      * @return {Object}
10210      */
10211     getValues : function(asString){
10212         //if (this.childForms) {
10213             // copy values from the child forms
10214         //    Roo.each(this.childForms, function (f) {
10215         //        this.setValues(f.getValues());
10216         //    }, this);
10217         //}
10218
10219
10220
10221         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10222         if(asString === true){
10223             return fs;
10224         }
10225         return Roo.urlDecode(fs);
10226     },
10227
10228     /**
10229      * Returns the fields in this form as an object with key/value pairs.
10230      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10231      * @return {Object}
10232      */
10233     getFieldValues : function(with_hidden)
10234     {
10235         var items = this.getItems();
10236         var ret = {};
10237         items.each(function(f){
10238             
10239             if (!f.getName()) {
10240                 return;
10241             }
10242             
10243             var v = f.getValue();
10244             
10245             if (f.inputType =='radio') {
10246                 if (typeof(ret[f.getName()]) == 'undefined') {
10247                     ret[f.getName()] = ''; // empty..
10248                 }
10249
10250                 if (!f.el.dom.checked) {
10251                     return;
10252
10253                 }
10254                 v = f.el.dom.value;
10255
10256             }
10257             
10258             if(f.xtype == 'MoneyField'){
10259                 ret[f.currencyName] = f.getCurrency();
10260             }
10261
10262             // not sure if this supported any more..
10263             if ((typeof(v) == 'object') && f.getRawValue) {
10264                 v = f.getRawValue() ; // dates..
10265             }
10266             // combo boxes where name != hiddenName...
10267             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10268                 ret[f.name] = f.getRawValue();
10269             }
10270             ret[f.getName()] = v;
10271         });
10272
10273         return ret;
10274     },
10275
10276     /**
10277      * Clears all invalid messages in this form.
10278      * @return {BasicForm} this
10279      */
10280     clearInvalid : function(){
10281         var items = this.getItems();
10282
10283         items.each(function(f){
10284            f.clearInvalid();
10285         });
10286
10287         return this;
10288     },
10289
10290     /**
10291      * Resets this form.
10292      * @return {BasicForm} this
10293      */
10294     reset : function(){
10295         var items = this.getItems();
10296         items.each(function(f){
10297             f.reset();
10298         });
10299
10300         Roo.each(this.childForms || [], function (f) {
10301             f.reset();
10302         });
10303
10304
10305         return this;
10306     },
10307     
10308     getItems : function()
10309     {
10310         var r=new Roo.util.MixedCollection(false, function(o){
10311             return o.id || (o.id = Roo.id());
10312         });
10313         var iter = function(el) {
10314             if (el.inputEl) {
10315                 r.add(el);
10316             }
10317             if (!el.items) {
10318                 return;
10319             }
10320             Roo.each(el.items,function(e) {
10321                 iter(e);
10322             });
10323         };
10324
10325         iter(this);
10326         return r;
10327     },
10328     
10329     hideFields : function(items)
10330     {
10331         Roo.each(items, function(i){
10332             
10333             var f = this.findField(i);
10334             
10335             if(!f){
10336                 return;
10337             }
10338             
10339             f.hide();
10340             
10341         }, this);
10342     },
10343     
10344     showFields : function(items)
10345     {
10346         Roo.each(items, function(i){
10347             
10348             var f = this.findField(i);
10349             
10350             if(!f){
10351                 return;
10352             }
10353             
10354             f.show();
10355             
10356         }, this);
10357     }
10358
10359 });
10360
10361 Roo.apply(Roo.bootstrap.Form, {
10362     
10363     popover : {
10364         
10365         padding : 5,
10366         
10367         isApplied : false,
10368         
10369         isMasked : false,
10370         
10371         form : false,
10372         
10373         target : false,
10374         
10375         toolTip : false,
10376         
10377         intervalID : false,
10378         
10379         maskEl : false,
10380         
10381         apply : function()
10382         {
10383             if(this.isApplied){
10384                 return;
10385             }
10386             
10387             this.maskEl = {
10388                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10389                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10390                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10391                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10392             };
10393             
10394             this.maskEl.top.enableDisplayMode("block");
10395             this.maskEl.left.enableDisplayMode("block");
10396             this.maskEl.bottom.enableDisplayMode("block");
10397             this.maskEl.right.enableDisplayMode("block");
10398             
10399             this.toolTip = new Roo.bootstrap.Tooltip({
10400                 cls : 'roo-form-error-popover',
10401                 alignment : {
10402                     'left' : ['r-l', [-2,0], 'right'],
10403                     'right' : ['l-r', [2,0], 'left'],
10404                     'bottom' : ['tl-bl', [0,2], 'top'],
10405                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10406                 }
10407             });
10408             
10409             this.toolTip.render(Roo.get(document.body));
10410
10411             this.toolTip.el.enableDisplayMode("block");
10412             
10413             Roo.get(document.body).on('click', function(){
10414                 this.unmask();
10415             }, this);
10416             
10417             Roo.get(document.body).on('touchstart', function(){
10418                 this.unmask();
10419             }, this);
10420             
10421             this.isApplied = true
10422         },
10423         
10424         mask : function(form, target)
10425         {
10426             this.form = form;
10427             
10428             this.target = target;
10429             
10430             if(!this.form.errorMask || !target.el){
10431                 return;
10432             }
10433             
10434             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10435             
10436             Roo.log(scrollable);
10437             
10438             var ot = this.target.el.calcOffsetsTo(scrollable);
10439             
10440             var scrollTo = ot[1] - this.form.maskOffset;
10441             
10442             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10443             
10444             scrollable.scrollTo('top', scrollTo);
10445             
10446             var box = this.target.el.getBox();
10447             Roo.log(box);
10448             var zIndex = Roo.bootstrap.Modal.zIndex++;
10449
10450             
10451             this.maskEl.top.setStyle('position', 'absolute');
10452             this.maskEl.top.setStyle('z-index', zIndex);
10453             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10454             this.maskEl.top.setLeft(0);
10455             this.maskEl.top.setTop(0);
10456             this.maskEl.top.show();
10457             
10458             this.maskEl.left.setStyle('position', 'absolute');
10459             this.maskEl.left.setStyle('z-index', zIndex);
10460             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10461             this.maskEl.left.setLeft(0);
10462             this.maskEl.left.setTop(box.y - this.padding);
10463             this.maskEl.left.show();
10464
10465             this.maskEl.bottom.setStyle('position', 'absolute');
10466             this.maskEl.bottom.setStyle('z-index', zIndex);
10467             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10468             this.maskEl.bottom.setLeft(0);
10469             this.maskEl.bottom.setTop(box.bottom + this.padding);
10470             this.maskEl.bottom.show();
10471
10472             this.maskEl.right.setStyle('position', 'absolute');
10473             this.maskEl.right.setStyle('z-index', zIndex);
10474             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10475             this.maskEl.right.setLeft(box.right + this.padding);
10476             this.maskEl.right.setTop(box.y - this.padding);
10477             this.maskEl.right.show();
10478
10479             this.toolTip.bindEl = this.target.el;
10480
10481             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10482
10483             var tip = this.target.blankText;
10484
10485             if(this.target.getValue() !== '' ) {
10486                 
10487                 if (this.target.invalidText.length) {
10488                     tip = this.target.invalidText;
10489                 } else if (this.target.regexText.length){
10490                     tip = this.target.regexText;
10491                 }
10492             }
10493
10494             this.toolTip.show(tip);
10495
10496             this.intervalID = window.setInterval(function() {
10497                 Roo.bootstrap.Form.popover.unmask();
10498             }, 10000);
10499
10500             window.onwheel = function(){ return false;};
10501             
10502             (function(){ this.isMasked = true; }).defer(500, this);
10503             
10504         },
10505         
10506         unmask : function()
10507         {
10508             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10509                 return;
10510             }
10511             
10512             this.maskEl.top.setStyle('position', 'absolute');
10513             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10514             this.maskEl.top.hide();
10515
10516             this.maskEl.left.setStyle('position', 'absolute');
10517             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10518             this.maskEl.left.hide();
10519
10520             this.maskEl.bottom.setStyle('position', 'absolute');
10521             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10522             this.maskEl.bottom.hide();
10523
10524             this.maskEl.right.setStyle('position', 'absolute');
10525             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10526             this.maskEl.right.hide();
10527             
10528             this.toolTip.hide();
10529             
10530             this.toolTip.el.hide();
10531             
10532             window.onwheel = function(){ return true;};
10533             
10534             if(this.intervalID){
10535                 window.clearInterval(this.intervalID);
10536                 this.intervalID = false;
10537             }
10538             
10539             this.isMasked = false;
10540             
10541         }
10542         
10543     }
10544     
10545 });
10546
10547 /*
10548  * Based on:
10549  * Ext JS Library 1.1.1
10550  * Copyright(c) 2006-2007, Ext JS, LLC.
10551  *
10552  * Originally Released Under LGPL - original licence link has changed is not relivant.
10553  *
10554  * Fork - LGPL
10555  * <script type="text/javascript">
10556  */
10557 /**
10558  * @class Roo.form.VTypes
10559  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10560  * @singleton
10561  */
10562 Roo.form.VTypes = function(){
10563     // closure these in so they are only created once.
10564     var alpha = /^[a-zA-Z_]+$/;
10565     var alphanum = /^[a-zA-Z0-9_]+$/;
10566     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10567     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10568
10569     // All these messages and functions are configurable
10570     return {
10571         /**
10572          * The function used to validate email addresses
10573          * @param {String} value The email address
10574          */
10575         'email' : function(v){
10576             return email.test(v);
10577         },
10578         /**
10579          * The error text to display when the email validation function returns false
10580          * @type String
10581          */
10582         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10583         /**
10584          * The keystroke filter mask to be applied on email input
10585          * @type RegExp
10586          */
10587         'emailMask' : /[a-z0-9_\.\-@]/i,
10588
10589         /**
10590          * The function used to validate URLs
10591          * @param {String} value The URL
10592          */
10593         'url' : function(v){
10594             return url.test(v);
10595         },
10596         /**
10597          * The error text to display when the url validation function returns false
10598          * @type String
10599          */
10600         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10601         
10602         /**
10603          * The function used to validate alpha values
10604          * @param {String} value The value
10605          */
10606         'alpha' : function(v){
10607             return alpha.test(v);
10608         },
10609         /**
10610          * The error text to display when the alpha validation function returns false
10611          * @type String
10612          */
10613         'alphaText' : 'This field should only contain letters and _',
10614         /**
10615          * The keystroke filter mask to be applied on alpha input
10616          * @type RegExp
10617          */
10618         'alphaMask' : /[a-z_]/i,
10619
10620         /**
10621          * The function used to validate alphanumeric values
10622          * @param {String} value The value
10623          */
10624         'alphanum' : function(v){
10625             return alphanum.test(v);
10626         },
10627         /**
10628          * The error text to display when the alphanumeric validation function returns false
10629          * @type String
10630          */
10631         'alphanumText' : 'This field should only contain letters, numbers and _',
10632         /**
10633          * The keystroke filter mask to be applied on alphanumeric input
10634          * @type RegExp
10635          */
10636         'alphanumMask' : /[a-z0-9_]/i
10637     };
10638 }();/*
10639  * - LGPL
10640  *
10641  * Input
10642  * 
10643  */
10644
10645 /**
10646  * @class Roo.bootstrap.Input
10647  * @extends Roo.bootstrap.Component
10648  * Bootstrap Input class
10649  * @cfg {Boolean} disabled is it disabled
10650  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10651  * @cfg {String} name name of the input
10652  * @cfg {string} fieldLabel - the label associated
10653  * @cfg {string} placeholder - placeholder to put in text.
10654  * @cfg {string}  before - input group add on before
10655  * @cfg {string} after - input group add on after
10656  * @cfg {string} size - (lg|sm) or leave empty..
10657  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10658  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10659  * @cfg {Number} md colspan out of 12 for computer-sized screens
10660  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10661  * @cfg {string} value default value of the input
10662  * @cfg {Number} labelWidth set the width of label 
10663  * @cfg {Number} labellg set the width of label (1-12)
10664  * @cfg {Number} labelmd set the width of label (1-12)
10665  * @cfg {Number} labelsm set the width of label (1-12)
10666  * @cfg {Number} labelxs set the width of label (1-12)
10667  * @cfg {String} labelAlign (top|left)
10668  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10669  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10670  * @cfg {String} indicatorpos (left|right) default left
10671  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10672  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10673  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10674
10675  * @cfg {String} align (left|center|right) Default left
10676  * @cfg {Boolean} forceFeedback (true|false) Default false
10677  * 
10678  * @constructor
10679  * Create a new Input
10680  * @param {Object} config The config object
10681  */
10682
10683 Roo.bootstrap.Input = function(config){
10684     
10685     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10686     
10687     this.addEvents({
10688         /**
10689          * @event focus
10690          * Fires when this field receives input focus.
10691          * @param {Roo.form.Field} this
10692          */
10693         focus : true,
10694         /**
10695          * @event blur
10696          * Fires when this field loses input focus.
10697          * @param {Roo.form.Field} this
10698          */
10699         blur : true,
10700         /**
10701          * @event specialkey
10702          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10703          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10704          * @param {Roo.form.Field} this
10705          * @param {Roo.EventObject} e The event object
10706          */
10707         specialkey : true,
10708         /**
10709          * @event change
10710          * Fires just before the field blurs if the field value has changed.
10711          * @param {Roo.form.Field} this
10712          * @param {Mixed} newValue The new value
10713          * @param {Mixed} oldValue The original value
10714          */
10715         change : true,
10716         /**
10717          * @event invalid
10718          * Fires after the field has been marked as invalid.
10719          * @param {Roo.form.Field} this
10720          * @param {String} msg The validation message
10721          */
10722         invalid : true,
10723         /**
10724          * @event valid
10725          * Fires after the field has been validated with no errors.
10726          * @param {Roo.form.Field} this
10727          */
10728         valid : true,
10729          /**
10730          * @event keyup
10731          * Fires after the key up
10732          * @param {Roo.form.Field} this
10733          * @param {Roo.EventObject}  e The event Object
10734          */
10735         keyup : true,
10736         /**
10737          * @event paste
10738          * Fires after the user pastes into input
10739          * @param {Roo.form.Field} this
10740          * @param {Roo.EventObject}  e The event Object
10741          */
10742         paste : true
10743     });
10744 };
10745
10746 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10747      /**
10748      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10749       automatic validation (defaults to "keyup").
10750      */
10751     validationEvent : "keyup",
10752      /**
10753      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10754      */
10755     validateOnBlur : true,
10756     /**
10757      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10758      */
10759     validationDelay : 250,
10760      /**
10761      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10762      */
10763     focusClass : "x-form-focus",  // not needed???
10764     
10765        
10766     /**
10767      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10768      */
10769     invalidClass : "has-warning",
10770     
10771     /**
10772      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10773      */
10774     validClass : "has-success",
10775     
10776     /**
10777      * @cfg {Boolean} hasFeedback (true|false) default true
10778      */
10779     hasFeedback : true,
10780     
10781     /**
10782      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10783      */
10784     invalidFeedbackClass : "glyphicon-warning-sign",
10785     
10786     /**
10787      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10788      */
10789     validFeedbackClass : "glyphicon-ok",
10790     
10791     /**
10792      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10793      */
10794     selectOnFocus : false,
10795     
10796      /**
10797      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10798      */
10799     maskRe : null,
10800        /**
10801      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10802      */
10803     vtype : null,
10804     
10805       /**
10806      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10807      */
10808     disableKeyFilter : false,
10809     
10810        /**
10811      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10812      */
10813     disabled : false,
10814      /**
10815      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10816      */
10817     allowBlank : true,
10818     /**
10819      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10820      */
10821     blankText : "Please complete this mandatory field",
10822     
10823      /**
10824      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10825      */
10826     minLength : 0,
10827     /**
10828      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10829      */
10830     maxLength : Number.MAX_VALUE,
10831     /**
10832      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10833      */
10834     minLengthText : "The minimum length for this field is {0}",
10835     /**
10836      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10837      */
10838     maxLengthText : "The maximum length for this field is {0}",
10839   
10840     
10841     /**
10842      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10843      * If available, this function will be called only after the basic validators all return true, and will be passed the
10844      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10845      */
10846     validator : null,
10847     /**
10848      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10849      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10850      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10851      */
10852     regex : null,
10853     /**
10854      * @cfg {String} regexText -- Depricated - use Invalid Text
10855      */
10856     regexText : "",
10857     
10858     /**
10859      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10860      */
10861     invalidText : "",
10862     
10863     
10864     
10865     autocomplete: false,
10866     
10867     
10868     fieldLabel : '',
10869     inputType : 'text',
10870     
10871     name : false,
10872     placeholder: false,
10873     before : false,
10874     after : false,
10875     size : false,
10876     hasFocus : false,
10877     preventMark: false,
10878     isFormField : true,
10879     value : '',
10880     labelWidth : 2,
10881     labelAlign : false,
10882     readOnly : false,
10883     align : false,
10884     formatedValue : false,
10885     forceFeedback : false,
10886     
10887     indicatorpos : 'left',
10888     
10889     labellg : 0,
10890     labelmd : 0,
10891     labelsm : 0,
10892     labelxs : 0,
10893     
10894     capture : '',
10895     accept : '',
10896     
10897     parentLabelAlign : function()
10898     {
10899         var parent = this;
10900         while (parent.parent()) {
10901             parent = parent.parent();
10902             if (typeof(parent.labelAlign) !='undefined') {
10903                 return parent.labelAlign;
10904             }
10905         }
10906         return 'left';
10907         
10908     },
10909     
10910     getAutoCreate : function()
10911     {
10912         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10913         
10914         var id = Roo.id();
10915         
10916         var cfg = {};
10917         
10918         if(this.inputType != 'hidden'){
10919             cfg.cls = 'form-group' //input-group
10920         }
10921         
10922         var input =  {
10923             tag: 'input',
10924             id : id,
10925             type : this.inputType,
10926             value : this.value,
10927             cls : 'form-control',
10928             placeholder : this.placeholder || '',
10929             autocomplete : this.autocomplete || 'new-password'
10930         };
10931         if (this.inputType == 'file') {
10932             input.style = 'overflow:hidden'; // why not in CSS?
10933         }
10934         
10935         if(this.capture.length){
10936             input.capture = this.capture;
10937         }
10938         
10939         if(this.accept.length){
10940             input.accept = this.accept + "/*";
10941         }
10942         
10943         if(this.align){
10944             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10945         }
10946         
10947         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10948             input.maxLength = this.maxLength;
10949         }
10950         
10951         if (this.disabled) {
10952             input.disabled=true;
10953         }
10954         
10955         if (this.readOnly) {
10956             input.readonly=true;
10957         }
10958         
10959         if (this.name) {
10960             input.name = this.name;
10961         }
10962         
10963         if (this.size) {
10964             input.cls += ' input-' + this.size;
10965         }
10966         
10967         var settings=this;
10968         ['xs','sm','md','lg'].map(function(size){
10969             if (settings[size]) {
10970                 cfg.cls += ' col-' + size + '-' + settings[size];
10971             }
10972         });
10973         
10974         var inputblock = input;
10975         
10976         var feedback = {
10977             tag: 'span',
10978             cls: 'glyphicon form-control-feedback'
10979         };
10980             
10981         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10982             
10983             inputblock = {
10984                 cls : 'has-feedback',
10985                 cn :  [
10986                     input,
10987                     feedback
10988                 ] 
10989             };  
10990         }
10991         
10992         if (this.before || this.after) {
10993             
10994             inputblock = {
10995                 cls : 'input-group',
10996                 cn :  [] 
10997             };
10998             
10999             if (this.before && typeof(this.before) == 'string') {
11000                 
11001                 inputblock.cn.push({
11002                     tag :'span',
11003                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11004                     html : this.before
11005                 });
11006             }
11007             if (this.before && typeof(this.before) == 'object') {
11008                 this.before = Roo.factory(this.before);
11009                 
11010                 inputblock.cn.push({
11011                     tag :'span',
11012                     cls : 'roo-input-before input-group-prepend   input-group-' +
11013                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11014                 });
11015             }
11016             
11017             inputblock.cn.push(input);
11018             
11019             if (this.after && typeof(this.after) == 'string') {
11020                 inputblock.cn.push({
11021                     tag :'span',
11022                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11023                     html : this.after
11024                 });
11025             }
11026             if (this.after && typeof(this.after) == 'object') {
11027                 this.after = Roo.factory(this.after);
11028                 
11029                 inputblock.cn.push({
11030                     tag :'span',
11031                     cls : 'roo-input-after input-group-append  input-group-' +
11032                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11033                 });
11034             }
11035             
11036             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11037                 inputblock.cls += ' has-feedback';
11038                 inputblock.cn.push(feedback);
11039             }
11040         };
11041         var indicator = {
11042             tag : 'i',
11043             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11044             tooltip : 'This field is required'
11045         };
11046         if (this.allowBlank ) {
11047             indicator.style = this.allowBlank ? ' display:none' : '';
11048         }
11049         if (align ==='left' && this.fieldLabel.length) {
11050             
11051             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11052             
11053             cfg.cn = [
11054                 indicator,
11055                 {
11056                     tag: 'label',
11057                     'for' :  id,
11058                     cls : 'control-label col-form-label',
11059                     html : this.fieldLabel
11060
11061                 },
11062                 {
11063                     cls : "", 
11064                     cn: [
11065                         inputblock
11066                     ]
11067                 }
11068             ];
11069             
11070             var labelCfg = cfg.cn[1];
11071             var contentCfg = cfg.cn[2];
11072             
11073             if(this.indicatorpos == 'right'){
11074                 cfg.cn = [
11075                     {
11076                         tag: 'label',
11077                         'for' :  id,
11078                         cls : 'control-label col-form-label',
11079                         cn : [
11080                             {
11081                                 tag : 'span',
11082                                 html : this.fieldLabel
11083                             },
11084                             indicator
11085                         ]
11086                     },
11087                     {
11088                         cls : "",
11089                         cn: [
11090                             inputblock
11091                         ]
11092                     }
11093
11094                 ];
11095                 
11096                 labelCfg = cfg.cn[0];
11097                 contentCfg = cfg.cn[1];
11098             
11099             }
11100             
11101             if(this.labelWidth > 12){
11102                 labelCfg.style = "width: " + this.labelWidth + 'px';
11103             }
11104             
11105             if(this.labelWidth < 13 && this.labelmd == 0){
11106                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11107             }
11108             
11109             if(this.labellg > 0){
11110                 labelCfg.cls += ' col-lg-' + this.labellg;
11111                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11112             }
11113             
11114             if(this.labelmd > 0){
11115                 labelCfg.cls += ' col-md-' + this.labelmd;
11116                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11117             }
11118             
11119             if(this.labelsm > 0){
11120                 labelCfg.cls += ' col-sm-' + this.labelsm;
11121                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11122             }
11123             
11124             if(this.labelxs > 0){
11125                 labelCfg.cls += ' col-xs-' + this.labelxs;
11126                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11127             }
11128             
11129             
11130         } else if ( this.fieldLabel.length) {
11131                 
11132             
11133             
11134             cfg.cn = [
11135                 {
11136                     tag : 'i',
11137                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11138                     tooltip : 'This field is required',
11139                     style : this.allowBlank ? ' display:none' : '' 
11140                 },
11141                 {
11142                     tag: 'label',
11143                    //cls : 'input-group-addon',
11144                     html : this.fieldLabel
11145
11146                 },
11147
11148                inputblock
11149
11150            ];
11151            
11152            if(this.indicatorpos == 'right'){
11153        
11154                 cfg.cn = [
11155                     {
11156                         tag: 'label',
11157                        //cls : 'input-group-addon',
11158                         html : this.fieldLabel
11159
11160                     },
11161                     {
11162                         tag : 'i',
11163                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11164                         tooltip : 'This field is required',
11165                         style : this.allowBlank ? ' display:none' : '' 
11166                     },
11167
11168                    inputblock
11169
11170                ];
11171
11172             }
11173
11174         } else {
11175             
11176             cfg.cn = [
11177
11178                     inputblock
11179
11180             ];
11181                 
11182                 
11183         };
11184         
11185         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11186            cfg.cls += ' navbar-form';
11187         }
11188         
11189         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11190             // on BS4 we do this only if not form 
11191             cfg.cls += ' navbar-form';
11192             cfg.tag = 'li';
11193         }
11194         
11195         return cfg;
11196         
11197     },
11198     /**
11199      * return the real input element.
11200      */
11201     inputEl: function ()
11202     {
11203         return this.el.select('input.form-control',true).first();
11204     },
11205     
11206     tooltipEl : function()
11207     {
11208         return this.inputEl();
11209     },
11210     
11211     indicatorEl : function()
11212     {
11213         if (Roo.bootstrap.version == 4) {
11214             return false; // not enabled in v4 yet.
11215         }
11216         
11217         var indicator = this.el.select('i.roo-required-indicator',true).first();
11218         
11219         if(!indicator){
11220             return false;
11221         }
11222         
11223         return indicator;
11224         
11225     },
11226     
11227     setDisabled : function(v)
11228     {
11229         var i  = this.inputEl().dom;
11230         if (!v) {
11231             i.removeAttribute('disabled');
11232             return;
11233             
11234         }
11235         i.setAttribute('disabled','true');
11236     },
11237     initEvents : function()
11238     {
11239           
11240         this.inputEl().on("keydown" , this.fireKey,  this);
11241         this.inputEl().on("focus", this.onFocus,  this);
11242         this.inputEl().on("blur", this.onBlur,  this);
11243         
11244         this.inputEl().relayEvent('keyup', this);
11245         this.inputEl().relayEvent('paste', this);
11246         
11247         this.indicator = this.indicatorEl();
11248         
11249         if(this.indicator){
11250             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11251         }
11252  
11253         // reference to original value for reset
11254         this.originalValue = this.getValue();
11255         //Roo.form.TextField.superclass.initEvents.call(this);
11256         if(this.validationEvent == 'keyup'){
11257             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11258             this.inputEl().on('keyup', this.filterValidation, this);
11259         }
11260         else if(this.validationEvent !== false){
11261             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11262         }
11263         
11264         if(this.selectOnFocus){
11265             this.on("focus", this.preFocus, this);
11266             
11267         }
11268         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11269             this.inputEl().on("keypress", this.filterKeys, this);
11270         } else {
11271             this.inputEl().relayEvent('keypress', this);
11272         }
11273        /* if(this.grow){
11274             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11275             this.el.on("click", this.autoSize,  this);
11276         }
11277         */
11278         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11279             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11280         }
11281         
11282         if (typeof(this.before) == 'object') {
11283             this.before.render(this.el.select('.roo-input-before',true).first());
11284         }
11285         if (typeof(this.after) == 'object') {
11286             this.after.render(this.el.select('.roo-input-after',true).first());
11287         }
11288         
11289         this.inputEl().on('change', this.onChange, this);
11290         
11291     },
11292     filterValidation : function(e){
11293         if(!e.isNavKeyPress()){
11294             this.validationTask.delay(this.validationDelay);
11295         }
11296     },
11297      /**
11298      * Validates the field value
11299      * @return {Boolean} True if the value is valid, else false
11300      */
11301     validate : function(){
11302         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11303         if(this.disabled || this.validateValue(this.getRawValue())){
11304             this.markValid();
11305             return true;
11306         }
11307         
11308         this.markInvalid();
11309         return false;
11310     },
11311     
11312     
11313     /**
11314      * Validates a value according to the field's validation rules and marks the field as invalid
11315      * if the validation fails
11316      * @param {Mixed} value The value to validate
11317      * @return {Boolean} True if the value is valid, else false
11318      */
11319     validateValue : function(value)
11320     {
11321         if(this.getVisibilityEl().hasClass('hidden')){
11322             return true;
11323         }
11324         
11325         if(value.length < 1)  { // if it's blank
11326             if(this.allowBlank){
11327                 return true;
11328             }
11329             return false;
11330         }
11331         
11332         if(value.length < this.minLength){
11333             return false;
11334         }
11335         if(value.length > this.maxLength){
11336             return false;
11337         }
11338         if(this.vtype){
11339             var vt = Roo.form.VTypes;
11340             if(!vt[this.vtype](value, this)){
11341                 return false;
11342             }
11343         }
11344         if(typeof this.validator == "function"){
11345             var msg = this.validator(value);
11346             if(msg !== true){
11347                 return false;
11348             }
11349             if (typeof(msg) == 'string') {
11350                 this.invalidText = msg;
11351             }
11352         }
11353         
11354         if(this.regex && !this.regex.test(value)){
11355             return false;
11356         }
11357         
11358         return true;
11359     },
11360     
11361      // private
11362     fireKey : function(e){
11363         //Roo.log('field ' + e.getKey());
11364         if(e.isNavKeyPress()){
11365             this.fireEvent("specialkey", this, e);
11366         }
11367     },
11368     focus : function (selectText){
11369         if(this.rendered){
11370             this.inputEl().focus();
11371             if(selectText === true){
11372                 this.inputEl().dom.select();
11373             }
11374         }
11375         return this;
11376     } ,
11377     
11378     onFocus : function(){
11379         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11380            // this.el.addClass(this.focusClass);
11381         }
11382         if(!this.hasFocus){
11383             this.hasFocus = true;
11384             this.startValue = this.getValue();
11385             this.fireEvent("focus", this);
11386         }
11387     },
11388     
11389     beforeBlur : Roo.emptyFn,
11390
11391     
11392     // private
11393     onBlur : function(){
11394         this.beforeBlur();
11395         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11396             //this.el.removeClass(this.focusClass);
11397         }
11398         this.hasFocus = false;
11399         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11400             this.validate();
11401         }
11402         var v = this.getValue();
11403         if(String(v) !== String(this.startValue)){
11404             this.fireEvent('change', this, v, this.startValue);
11405         }
11406         this.fireEvent("blur", this);
11407     },
11408     
11409     onChange : function(e)
11410     {
11411         var v = this.getValue();
11412         if(String(v) !== String(this.startValue)){
11413             this.fireEvent('change', this, v, this.startValue);
11414         }
11415         
11416     },
11417     
11418     /**
11419      * Resets the current field value to the originally loaded value and clears any validation messages
11420      */
11421     reset : function(){
11422         this.setValue(this.originalValue);
11423         this.validate();
11424     },
11425      /**
11426      * Returns the name of the field
11427      * @return {Mixed} name The name field
11428      */
11429     getName: function(){
11430         return this.name;
11431     },
11432      /**
11433      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11434      * @return {Mixed} value The field value
11435      */
11436     getValue : function(){
11437         
11438         var v = this.inputEl().getValue();
11439         
11440         return v;
11441     },
11442     /**
11443      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11444      * @return {Mixed} value The field value
11445      */
11446     getRawValue : function(){
11447         var v = this.inputEl().getValue();
11448         
11449         return v;
11450     },
11451     
11452     /**
11453      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11454      * @param {Mixed} value The value to set
11455      */
11456     setRawValue : function(v){
11457         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11458     },
11459     
11460     selectText : function(start, end){
11461         var v = this.getRawValue();
11462         if(v.length > 0){
11463             start = start === undefined ? 0 : start;
11464             end = end === undefined ? v.length : end;
11465             var d = this.inputEl().dom;
11466             if(d.setSelectionRange){
11467                 d.setSelectionRange(start, end);
11468             }else if(d.createTextRange){
11469                 var range = d.createTextRange();
11470                 range.moveStart("character", start);
11471                 range.moveEnd("character", v.length-end);
11472                 range.select();
11473             }
11474         }
11475     },
11476     
11477     /**
11478      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11479      * @param {Mixed} value The value to set
11480      */
11481     setValue : function(v){
11482         this.value = v;
11483         if(this.rendered){
11484             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11485             this.validate();
11486         }
11487     },
11488     
11489     /*
11490     processValue : function(value){
11491         if(this.stripCharsRe){
11492             var newValue = value.replace(this.stripCharsRe, '');
11493             if(newValue !== value){
11494                 this.setRawValue(newValue);
11495                 return newValue;
11496             }
11497         }
11498         return value;
11499     },
11500   */
11501     preFocus : function(){
11502         
11503         if(this.selectOnFocus){
11504             this.inputEl().dom.select();
11505         }
11506     },
11507     filterKeys : function(e){
11508         var k = e.getKey();
11509         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11510             return;
11511         }
11512         var c = e.getCharCode(), cc = String.fromCharCode(c);
11513         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11514             return;
11515         }
11516         if(!this.maskRe.test(cc)){
11517             e.stopEvent();
11518         }
11519     },
11520      /**
11521      * Clear any invalid styles/messages for this field
11522      */
11523     clearInvalid : function(){
11524         
11525         if(!this.el || this.preventMark){ // not rendered
11526             return;
11527         }
11528         
11529         
11530         this.el.removeClass([this.invalidClass, 'is-invalid']);
11531         
11532         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11533             
11534             var feedback = this.el.select('.form-control-feedback', true).first();
11535             
11536             if(feedback){
11537                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11538             }
11539             
11540         }
11541         
11542         if(this.indicator){
11543             this.indicator.removeClass('visible');
11544             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11545         }
11546         
11547         this.fireEvent('valid', this);
11548     },
11549     
11550      /**
11551      * Mark this field as valid
11552      */
11553     markValid : function()
11554     {
11555         if(!this.el  || this.preventMark){ // not rendered...
11556             return;
11557         }
11558         
11559         this.el.removeClass([this.invalidClass, this.validClass]);
11560         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11561
11562         var feedback = this.el.select('.form-control-feedback', true).first();
11563             
11564         if(feedback){
11565             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11566         }
11567         
11568         if(this.indicator){
11569             this.indicator.removeClass('visible');
11570             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11571         }
11572         
11573         if(this.disabled){
11574             return;
11575         }
11576         
11577            
11578         if(this.allowBlank && !this.getRawValue().length){
11579             return;
11580         }
11581         if (Roo.bootstrap.version == 3) {
11582             this.el.addClass(this.validClass);
11583         } else {
11584             this.inputEl().addClass('is-valid');
11585         }
11586
11587         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11588             
11589             var feedback = this.el.select('.form-control-feedback', true).first();
11590             
11591             if(feedback){
11592                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11593                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11594             }
11595             
11596         }
11597         
11598         this.fireEvent('valid', this);
11599     },
11600     
11601      /**
11602      * Mark this field as invalid
11603      * @param {String} msg The validation message
11604      */
11605     markInvalid : function(msg)
11606     {
11607         if(!this.el  || this.preventMark){ // not rendered
11608             return;
11609         }
11610         
11611         this.el.removeClass([this.invalidClass, this.validClass]);
11612         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11613         
11614         var feedback = this.el.select('.form-control-feedback', true).first();
11615             
11616         if(feedback){
11617             this.el.select('.form-control-feedback', true).first().removeClass(
11618                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11619         }
11620
11621         if(this.disabled){
11622             return;
11623         }
11624         
11625         if(this.allowBlank && !this.getRawValue().length){
11626             return;
11627         }
11628         
11629         if(this.indicator){
11630             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11631             this.indicator.addClass('visible');
11632         }
11633         if (Roo.bootstrap.version == 3) {
11634             this.el.addClass(this.invalidClass);
11635         } else {
11636             this.inputEl().addClass('is-invalid');
11637         }
11638         
11639         
11640         
11641         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11642             
11643             var feedback = this.el.select('.form-control-feedback', true).first();
11644             
11645             if(feedback){
11646                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11647                 
11648                 if(this.getValue().length || this.forceFeedback){
11649                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11650                 }
11651                 
11652             }
11653             
11654         }
11655         
11656         this.fireEvent('invalid', this, msg);
11657     },
11658     // private
11659     SafariOnKeyDown : function(event)
11660     {
11661         // this is a workaround for a password hang bug on chrome/ webkit.
11662         if (this.inputEl().dom.type != 'password') {
11663             return;
11664         }
11665         
11666         var isSelectAll = false;
11667         
11668         if(this.inputEl().dom.selectionEnd > 0){
11669             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11670         }
11671         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11672             event.preventDefault();
11673             this.setValue('');
11674             return;
11675         }
11676         
11677         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11678             
11679             event.preventDefault();
11680             // this is very hacky as keydown always get's upper case.
11681             //
11682             var cc = String.fromCharCode(event.getCharCode());
11683             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11684             
11685         }
11686     },
11687     adjustWidth : function(tag, w){
11688         tag = tag.toLowerCase();
11689         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11690             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11691                 if(tag == 'input'){
11692                     return w + 2;
11693                 }
11694                 if(tag == 'textarea'){
11695                     return w-2;
11696                 }
11697             }else if(Roo.isOpera){
11698                 if(tag == 'input'){
11699                     return w + 2;
11700                 }
11701                 if(tag == 'textarea'){
11702                     return w-2;
11703                 }
11704             }
11705         }
11706         return w;
11707     },
11708     
11709     setFieldLabel : function(v)
11710     {
11711         if(!this.rendered){
11712             return;
11713         }
11714         
11715         if(this.indicatorEl()){
11716             var ar = this.el.select('label > span',true);
11717             
11718             if (ar.elements.length) {
11719                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11720                 this.fieldLabel = v;
11721                 return;
11722             }
11723             
11724             var br = this.el.select('label',true);
11725             
11726             if(br.elements.length) {
11727                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11728                 this.fieldLabel = v;
11729                 return;
11730             }
11731             
11732             Roo.log('Cannot Found any of label > span || label in input');
11733             return;
11734         }
11735         
11736         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11737         this.fieldLabel = v;
11738         
11739         
11740     }
11741 });
11742
11743  
11744 /*
11745  * - LGPL
11746  *
11747  * Input
11748  * 
11749  */
11750
11751 /**
11752  * @class Roo.bootstrap.TextArea
11753  * @extends Roo.bootstrap.Input
11754  * Bootstrap TextArea class
11755  * @cfg {Number} cols Specifies the visible width of a text area
11756  * @cfg {Number} rows Specifies the visible number of lines in a text area
11757  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11758  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11759  * @cfg {string} html text
11760  * 
11761  * @constructor
11762  * Create a new TextArea
11763  * @param {Object} config The config object
11764  */
11765
11766 Roo.bootstrap.TextArea = function(config){
11767     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11768    
11769 };
11770
11771 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11772      
11773     cols : false,
11774     rows : 5,
11775     readOnly : false,
11776     warp : 'soft',
11777     resize : false,
11778     value: false,
11779     html: false,
11780     
11781     getAutoCreate : function(){
11782         
11783         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11784         
11785         var id = Roo.id();
11786         
11787         var cfg = {};
11788         
11789         if(this.inputType != 'hidden'){
11790             cfg.cls = 'form-group' //input-group
11791         }
11792         
11793         var input =  {
11794             tag: 'textarea',
11795             id : id,
11796             warp : this.warp,
11797             rows : this.rows,
11798             value : this.value || '',
11799             html: this.html || '',
11800             cls : 'form-control',
11801             placeholder : this.placeholder || '' 
11802             
11803         };
11804         
11805         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11806             input.maxLength = this.maxLength;
11807         }
11808         
11809         if(this.resize){
11810             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11811         }
11812         
11813         if(this.cols){
11814             input.cols = this.cols;
11815         }
11816         
11817         if (this.readOnly) {
11818             input.readonly = true;
11819         }
11820         
11821         if (this.name) {
11822             input.name = this.name;
11823         }
11824         
11825         if (this.size) {
11826             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11827         }
11828         
11829         var settings=this;
11830         ['xs','sm','md','lg'].map(function(size){
11831             if (settings[size]) {
11832                 cfg.cls += ' col-' + size + '-' + settings[size];
11833             }
11834         });
11835         
11836         var inputblock = input;
11837         
11838         if(this.hasFeedback && !this.allowBlank){
11839             
11840             var feedback = {
11841                 tag: 'span',
11842                 cls: 'glyphicon form-control-feedback'
11843             };
11844
11845             inputblock = {
11846                 cls : 'has-feedback',
11847                 cn :  [
11848                     input,
11849                     feedback
11850                 ] 
11851             };  
11852         }
11853         
11854         
11855         if (this.before || this.after) {
11856             
11857             inputblock = {
11858                 cls : 'input-group',
11859                 cn :  [] 
11860             };
11861             if (this.before) {
11862                 inputblock.cn.push({
11863                     tag :'span',
11864                     cls : 'input-group-addon',
11865                     html : this.before
11866                 });
11867             }
11868             
11869             inputblock.cn.push(input);
11870             
11871             if(this.hasFeedback && !this.allowBlank){
11872                 inputblock.cls += ' has-feedback';
11873                 inputblock.cn.push(feedback);
11874             }
11875             
11876             if (this.after) {
11877                 inputblock.cn.push({
11878                     tag :'span',
11879                     cls : 'input-group-addon',
11880                     html : this.after
11881                 });
11882             }
11883             
11884         }
11885         
11886         if (align ==='left' && this.fieldLabel.length) {
11887             cfg.cn = [
11888                 {
11889                     tag: 'label',
11890                     'for' :  id,
11891                     cls : 'control-label',
11892                     html : this.fieldLabel
11893                 },
11894                 {
11895                     cls : "",
11896                     cn: [
11897                         inputblock
11898                     ]
11899                 }
11900
11901             ];
11902             
11903             if(this.labelWidth > 12){
11904                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11905             }
11906
11907             if(this.labelWidth < 13 && this.labelmd == 0){
11908                 this.labelmd = this.labelWidth;
11909             }
11910
11911             if(this.labellg > 0){
11912                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11913                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11914             }
11915
11916             if(this.labelmd > 0){
11917                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11918                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11919             }
11920
11921             if(this.labelsm > 0){
11922                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11923                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11924             }
11925
11926             if(this.labelxs > 0){
11927                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11928                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11929             }
11930             
11931         } else if ( this.fieldLabel.length) {
11932             cfg.cn = [
11933
11934                {
11935                    tag: 'label',
11936                    //cls : 'input-group-addon',
11937                    html : this.fieldLabel
11938
11939                },
11940
11941                inputblock
11942
11943            ];
11944
11945         } else {
11946
11947             cfg.cn = [
11948
11949                 inputblock
11950
11951             ];
11952                 
11953         }
11954         
11955         if (this.disabled) {
11956             input.disabled=true;
11957         }
11958         
11959         return cfg;
11960         
11961     },
11962     /**
11963      * return the real textarea element.
11964      */
11965     inputEl: function ()
11966     {
11967         return this.el.select('textarea.form-control',true).first();
11968     },
11969     
11970     /**
11971      * Clear any invalid styles/messages for this field
11972      */
11973     clearInvalid : function()
11974     {
11975         
11976         if(!this.el || this.preventMark){ // not rendered
11977             return;
11978         }
11979         
11980         var label = this.el.select('label', true).first();
11981         var icon = this.el.select('i.fa-star', true).first();
11982         
11983         if(label && icon){
11984             icon.remove();
11985         }
11986         this.el.removeClass( this.validClass);
11987         this.inputEl().removeClass('is-invalid');
11988          
11989         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11990             
11991             var feedback = this.el.select('.form-control-feedback', true).first();
11992             
11993             if(feedback){
11994                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11995             }
11996             
11997         }
11998         
11999         this.fireEvent('valid', this);
12000     },
12001     
12002      /**
12003      * Mark this field as valid
12004      */
12005     markValid : function()
12006     {
12007         if(!this.el  || this.preventMark){ // not rendered
12008             return;
12009         }
12010         
12011         this.el.removeClass([this.invalidClass, this.validClass]);
12012         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12013         
12014         var feedback = this.el.select('.form-control-feedback', true).first();
12015             
12016         if(feedback){
12017             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12018         }
12019
12020         if(this.disabled || this.allowBlank){
12021             return;
12022         }
12023         
12024         var label = this.el.select('label', true).first();
12025         var icon = this.el.select('i.fa-star', true).first();
12026         
12027         if(label && icon){
12028             icon.remove();
12029         }
12030         if (Roo.bootstrap.version == 3) {
12031             this.el.addClass(this.validClass);
12032         } else {
12033             this.inputEl().addClass('is-valid');
12034         }
12035         
12036         
12037         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12038             
12039             var feedback = this.el.select('.form-control-feedback', true).first();
12040             
12041             if(feedback){
12042                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12043                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12044             }
12045             
12046         }
12047         
12048         this.fireEvent('valid', this);
12049     },
12050     
12051      /**
12052      * Mark this field as invalid
12053      * @param {String} msg The validation message
12054      */
12055     markInvalid : function(msg)
12056     {
12057         if(!this.el  || this.preventMark){ // not rendered
12058             return;
12059         }
12060         
12061         this.el.removeClass([this.invalidClass, this.validClass]);
12062         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12063         
12064         var feedback = this.el.select('.form-control-feedback', true).first();
12065             
12066         if(feedback){
12067             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12068         }
12069
12070         if(this.disabled || this.allowBlank){
12071             return;
12072         }
12073         
12074         var label = this.el.select('label', true).first();
12075         var icon = this.el.select('i.fa-star', true).first();
12076         
12077         if(!this.getValue().length && label && !icon){
12078             this.el.createChild({
12079                 tag : 'i',
12080                 cls : 'text-danger fa fa-lg fa-star',
12081                 tooltip : 'This field is required',
12082                 style : 'margin-right:5px;'
12083             }, label, true);
12084         }
12085         
12086         if (Roo.bootstrap.version == 3) {
12087             this.el.addClass(this.invalidClass);
12088         } else {
12089             this.inputEl().addClass('is-invalid');
12090         }
12091         
12092         // fixme ... this may be depricated need to test..
12093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12094             
12095             var feedback = this.el.select('.form-control-feedback', true).first();
12096             
12097             if(feedback){
12098                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12099                 
12100                 if(this.getValue().length || this.forceFeedback){
12101                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12102                 }
12103                 
12104             }
12105             
12106         }
12107         
12108         this.fireEvent('invalid', this, msg);
12109     }
12110 });
12111
12112  
12113 /*
12114  * - LGPL
12115  *
12116  * trigger field - base class for combo..
12117  * 
12118  */
12119  
12120 /**
12121  * @class Roo.bootstrap.TriggerField
12122  * @extends Roo.bootstrap.Input
12123  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12124  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12125  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12126  * for which you can provide a custom implementation.  For example:
12127  * <pre><code>
12128 var trigger = new Roo.bootstrap.TriggerField();
12129 trigger.onTriggerClick = myTriggerFn;
12130 trigger.applyTo('my-field');
12131 </code></pre>
12132  *
12133  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12134  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12135  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12136  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12137  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12138
12139  * @constructor
12140  * Create a new TriggerField.
12141  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12142  * to the base TextField)
12143  */
12144 Roo.bootstrap.TriggerField = function(config){
12145     this.mimicing = false;
12146     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12147 };
12148
12149 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12150     /**
12151      * @cfg {String} triggerClass A CSS class to apply to the trigger
12152      */
12153      /**
12154      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12155      */
12156     hideTrigger:false,
12157
12158     /**
12159      * @cfg {Boolean} removable (true|false) special filter default false
12160      */
12161     removable : false,
12162     
12163     /** @cfg {Boolean} grow @hide */
12164     /** @cfg {Number} growMin @hide */
12165     /** @cfg {Number} growMax @hide */
12166
12167     /**
12168      * @hide 
12169      * @method
12170      */
12171     autoSize: Roo.emptyFn,
12172     // private
12173     monitorTab : true,
12174     // private
12175     deferHeight : true,
12176
12177     
12178     actionMode : 'wrap',
12179     
12180     caret : false,
12181     
12182     
12183     getAutoCreate : function(){
12184        
12185         var align = this.labelAlign || this.parentLabelAlign();
12186         
12187         var id = Roo.id();
12188         
12189         var cfg = {
12190             cls: 'form-group' //input-group
12191         };
12192         
12193         
12194         var input =  {
12195             tag: 'input',
12196             id : id,
12197             type : this.inputType,
12198             cls : 'form-control',
12199             autocomplete: 'new-password',
12200             placeholder : this.placeholder || '' 
12201             
12202         };
12203         if (this.name) {
12204             input.name = this.name;
12205         }
12206         if (this.size) {
12207             input.cls += ' input-' + this.size;
12208         }
12209         
12210         if (this.disabled) {
12211             input.disabled=true;
12212         }
12213         
12214         var inputblock = input;
12215         
12216         if(this.hasFeedback && !this.allowBlank){
12217             
12218             var feedback = {
12219                 tag: 'span',
12220                 cls: 'glyphicon form-control-feedback'
12221             };
12222             
12223             if(this.removable && !this.editable  ){
12224                 inputblock = {
12225                     cls : 'has-feedback',
12226                     cn :  [
12227                         inputblock,
12228                         {
12229                             tag: 'button',
12230                             html : 'x',
12231                             cls : 'roo-combo-removable-btn close'
12232                         },
12233                         feedback
12234                     ] 
12235                 };
12236             } else {
12237                 inputblock = {
12238                     cls : 'has-feedback',
12239                     cn :  [
12240                         inputblock,
12241                         feedback
12242                     ] 
12243                 };
12244             }
12245
12246         } else {
12247             if(this.removable && !this.editable ){
12248                 inputblock = {
12249                     cls : 'roo-removable',
12250                     cn :  [
12251                         inputblock,
12252                         {
12253                             tag: 'button',
12254                             html : 'x',
12255                             cls : 'roo-combo-removable-btn close'
12256                         }
12257                     ] 
12258                 };
12259             }
12260         }
12261         
12262         if (this.before || this.after) {
12263             
12264             inputblock = {
12265                 cls : 'input-group',
12266                 cn :  [] 
12267             };
12268             if (this.before) {
12269                 inputblock.cn.push({
12270                     tag :'span',
12271                     cls : 'input-group-addon input-group-prepend input-group-text',
12272                     html : this.before
12273                 });
12274             }
12275             
12276             inputblock.cn.push(input);
12277             
12278             if(this.hasFeedback && !this.allowBlank){
12279                 inputblock.cls += ' has-feedback';
12280                 inputblock.cn.push(feedback);
12281             }
12282             
12283             if (this.after) {
12284                 inputblock.cn.push({
12285                     tag :'span',
12286                     cls : 'input-group-addon input-group-append input-group-text',
12287                     html : this.after
12288                 });
12289             }
12290             
12291         };
12292         
12293       
12294         
12295         var ibwrap = inputblock;
12296         
12297         if(this.multiple){
12298             ibwrap = {
12299                 tag: 'ul',
12300                 cls: 'roo-select2-choices',
12301                 cn:[
12302                     {
12303                         tag: 'li',
12304                         cls: 'roo-select2-search-field',
12305                         cn: [
12306
12307                             inputblock
12308                         ]
12309                     }
12310                 ]
12311             };
12312                 
12313         }
12314         
12315         var combobox = {
12316             cls: 'roo-select2-container input-group',
12317             cn: [
12318                  {
12319                     tag: 'input',
12320                     type : 'hidden',
12321                     cls: 'form-hidden-field'
12322                 },
12323                 ibwrap
12324             ]
12325         };
12326         
12327         if(!this.multiple && this.showToggleBtn){
12328             
12329             var caret = {
12330                         tag: 'span',
12331                         cls: 'caret'
12332              };
12333             if (this.caret != false) {
12334                 caret = {
12335                      tag: 'i',
12336                      cls: 'fa fa-' + this.caret
12337                 };
12338                 
12339             }
12340             
12341             combobox.cn.push({
12342                 tag :'span',
12343                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12344                 cn : [
12345                     Roo.bootstrap.version == 3 ? caret : '',
12346                     {
12347                         tag: 'span',
12348                         cls: 'combobox-clear',
12349                         cn  : [
12350                             {
12351                                 tag : 'i',
12352                                 cls: 'icon-remove'
12353                             }
12354                         ]
12355                     }
12356                 ]
12357
12358             })
12359         }
12360         
12361         if(this.multiple){
12362             combobox.cls += ' roo-select2-container-multi';
12363         }
12364          var indicator = {
12365             tag : 'i',
12366             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12367             tooltip : 'This field is required'
12368         };
12369         if (Roo.bootstrap.version == 4) {
12370             indicator = {
12371                 tag : 'i',
12372                 style : 'display:none'
12373             };
12374         }
12375         
12376         
12377         if (align ==='left' && this.fieldLabel.length) {
12378             
12379             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12380
12381             cfg.cn = [
12382                 indicator,
12383                 {
12384                     tag: 'label',
12385                     'for' :  id,
12386                     cls : 'control-label',
12387                     html : this.fieldLabel
12388
12389                 },
12390                 {
12391                     cls : "", 
12392                     cn: [
12393                         combobox
12394                     ]
12395                 }
12396
12397             ];
12398             
12399             var labelCfg = cfg.cn[1];
12400             var contentCfg = cfg.cn[2];
12401             
12402             if(this.indicatorpos == 'right'){
12403                 cfg.cn = [
12404                     {
12405                         tag: 'label',
12406                         'for' :  id,
12407                         cls : 'control-label',
12408                         cn : [
12409                             {
12410                                 tag : 'span',
12411                                 html : this.fieldLabel
12412                             },
12413                             indicator
12414                         ]
12415                     },
12416                     {
12417                         cls : "", 
12418                         cn: [
12419                             combobox
12420                         ]
12421                     }
12422
12423                 ];
12424                 
12425                 labelCfg = cfg.cn[0];
12426                 contentCfg = cfg.cn[1];
12427             }
12428             
12429             if(this.labelWidth > 12){
12430                 labelCfg.style = "width: " + this.labelWidth + 'px';
12431             }
12432             
12433             if(this.labelWidth < 13 && this.labelmd == 0){
12434                 this.labelmd = this.labelWidth;
12435             }
12436             
12437             if(this.labellg > 0){
12438                 labelCfg.cls += ' col-lg-' + this.labellg;
12439                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12440             }
12441             
12442             if(this.labelmd > 0){
12443                 labelCfg.cls += ' col-md-' + this.labelmd;
12444                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12445             }
12446             
12447             if(this.labelsm > 0){
12448                 labelCfg.cls += ' col-sm-' + this.labelsm;
12449                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12450             }
12451             
12452             if(this.labelxs > 0){
12453                 labelCfg.cls += ' col-xs-' + this.labelxs;
12454                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12455             }
12456             
12457         } else if ( this.fieldLabel.length) {
12458 //                Roo.log(" label");
12459             cfg.cn = [
12460                 indicator,
12461                {
12462                    tag: 'label',
12463                    //cls : 'input-group-addon',
12464                    html : this.fieldLabel
12465
12466                },
12467
12468                combobox
12469
12470             ];
12471             
12472             if(this.indicatorpos == 'right'){
12473                 
12474                 cfg.cn = [
12475                     {
12476                        tag: 'label',
12477                        cn : [
12478                            {
12479                                tag : 'span',
12480                                html : this.fieldLabel
12481                            },
12482                            indicator
12483                        ]
12484
12485                     },
12486                     combobox
12487
12488                 ];
12489
12490             }
12491
12492         } else {
12493             
12494 //                Roo.log(" no label && no align");
12495                 cfg = combobox
12496                      
12497                 
12498         }
12499         
12500         var settings=this;
12501         ['xs','sm','md','lg'].map(function(size){
12502             if (settings[size]) {
12503                 cfg.cls += ' col-' + size + '-' + settings[size];
12504             }
12505         });
12506         
12507         return cfg;
12508         
12509     },
12510     
12511     
12512     
12513     // private
12514     onResize : function(w, h){
12515 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12516 //        if(typeof w == 'number'){
12517 //            var x = w - this.trigger.getWidth();
12518 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12519 //            this.trigger.setStyle('left', x+'px');
12520 //        }
12521     },
12522
12523     // private
12524     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12525
12526     // private
12527     getResizeEl : function(){
12528         return this.inputEl();
12529     },
12530
12531     // private
12532     getPositionEl : function(){
12533         return this.inputEl();
12534     },
12535
12536     // private
12537     alignErrorIcon : function(){
12538         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12539     },
12540
12541     // private
12542     initEvents : function(){
12543         
12544         this.createList();
12545         
12546         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12547         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12548         if(!this.multiple && this.showToggleBtn){
12549             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12550             if(this.hideTrigger){
12551                 this.trigger.setDisplayed(false);
12552             }
12553             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12554         }
12555         
12556         if(this.multiple){
12557             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12558         }
12559         
12560         if(this.removable && !this.editable && !this.tickable){
12561             var close = this.closeTriggerEl();
12562             
12563             if(close){
12564                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12565                 close.on('click', this.removeBtnClick, this, close);
12566             }
12567         }
12568         
12569         //this.trigger.addClassOnOver('x-form-trigger-over');
12570         //this.trigger.addClassOnClick('x-form-trigger-click');
12571         
12572         //if(!this.width){
12573         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12574         //}
12575     },
12576     
12577     closeTriggerEl : function()
12578     {
12579         var close = this.el.select('.roo-combo-removable-btn', true).first();
12580         return close ? close : false;
12581     },
12582     
12583     removeBtnClick : function(e, h, el)
12584     {
12585         e.preventDefault();
12586         
12587         if(this.fireEvent("remove", this) !== false){
12588             this.reset();
12589             this.fireEvent("afterremove", this)
12590         }
12591     },
12592     
12593     createList : function()
12594     {
12595         this.list = Roo.get(document.body).createChild({
12596             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12597             cls: 'typeahead typeahead-long dropdown-menu shadow',
12598             style: 'display:none'
12599         });
12600         
12601         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12602         
12603     },
12604
12605     // private
12606     initTrigger : function(){
12607        
12608     },
12609
12610     // private
12611     onDestroy : function(){
12612         if(this.trigger){
12613             this.trigger.removeAllListeners();
12614           //  this.trigger.remove();
12615         }
12616         //if(this.wrap){
12617         //    this.wrap.remove();
12618         //}
12619         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12620     },
12621
12622     // private
12623     onFocus : function(){
12624         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12625         /*
12626         if(!this.mimicing){
12627             this.wrap.addClass('x-trigger-wrap-focus');
12628             this.mimicing = true;
12629             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12630             if(this.monitorTab){
12631                 this.el.on("keydown", this.checkTab, this);
12632             }
12633         }
12634         */
12635     },
12636
12637     // private
12638     checkTab : function(e){
12639         if(e.getKey() == e.TAB){
12640             this.triggerBlur();
12641         }
12642     },
12643
12644     // private
12645     onBlur : function(){
12646         // do nothing
12647     },
12648
12649     // private
12650     mimicBlur : function(e, t){
12651         /*
12652         if(!this.wrap.contains(t) && this.validateBlur()){
12653             this.triggerBlur();
12654         }
12655         */
12656     },
12657
12658     // private
12659     triggerBlur : function(){
12660         this.mimicing = false;
12661         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12662         if(this.monitorTab){
12663             this.el.un("keydown", this.checkTab, this);
12664         }
12665         //this.wrap.removeClass('x-trigger-wrap-focus');
12666         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12667     },
12668
12669     // private
12670     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12671     validateBlur : function(e, t){
12672         return true;
12673     },
12674
12675     // private
12676     onDisable : function(){
12677         this.inputEl().dom.disabled = true;
12678         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12679         //if(this.wrap){
12680         //    this.wrap.addClass('x-item-disabled');
12681         //}
12682     },
12683
12684     // private
12685     onEnable : function(){
12686         this.inputEl().dom.disabled = false;
12687         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12688         //if(this.wrap){
12689         //    this.el.removeClass('x-item-disabled');
12690         //}
12691     },
12692
12693     // private
12694     onShow : function(){
12695         var ae = this.getActionEl();
12696         
12697         if(ae){
12698             ae.dom.style.display = '';
12699             ae.dom.style.visibility = 'visible';
12700         }
12701     },
12702
12703     // private
12704     
12705     onHide : function(){
12706         var ae = this.getActionEl();
12707         ae.dom.style.display = 'none';
12708     },
12709
12710     /**
12711      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12712      * by an implementing function.
12713      * @method
12714      * @param {EventObject} e
12715      */
12716     onTriggerClick : Roo.emptyFn
12717 });
12718  
12719 /*
12720 * Licence: LGPL
12721 */
12722
12723 /**
12724  * @class Roo.bootstrap.CardUploader
12725  * @extends Roo.bootstrap.Button
12726  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12727  * @cfg {Number} errorTimeout default 3000
12728  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12729  * @cfg {Array}  html The button text.
12730
12731  *
12732  * @constructor
12733  * Create a new CardUploader
12734  * @param {Object} config The config object
12735  */
12736
12737 Roo.bootstrap.CardUploader = function(config){
12738     
12739  
12740     
12741     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12742     
12743     
12744     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12745         return r.data.id
12746      });
12747     
12748      this.addEvents({
12749          // raw events
12750         /**
12751          * @event preview
12752          * When a image is clicked on - and needs to display a slideshow or similar..
12753          * @param {Roo.bootstrap.Card} this
12754          * @param {Object} The image information data 
12755          *
12756          */
12757         'preview' : true,
12758          /**
12759          * @event download
12760          * When a the download link is clicked
12761          * @param {Roo.bootstrap.Card} this
12762          * @param {Object} The image information data  contains 
12763          */
12764         'download' : true
12765         
12766     });
12767 };
12768  
12769 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12770     
12771      
12772     errorTimeout : 3000,
12773      
12774     images : false,
12775    
12776     fileCollection : false,
12777     allowBlank : true,
12778     
12779     getAutoCreate : function()
12780     {
12781         
12782         var cfg =  {
12783             cls :'form-group' ,
12784             cn : [
12785                
12786                 {
12787                     tag: 'label',
12788                    //cls : 'input-group-addon',
12789                     html : this.fieldLabel
12790
12791                 },
12792
12793                 {
12794                     tag: 'input',
12795                     type : 'hidden',
12796                     name : this.name,
12797                     value : this.value,
12798                     cls : 'd-none  form-control'
12799                 },
12800                 
12801                 {
12802                     tag: 'input',
12803                     multiple : 'multiple',
12804                     type : 'file',
12805                     cls : 'd-none  roo-card-upload-selector'
12806                 },
12807                 
12808                 {
12809                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12810                 },
12811                 {
12812                     cls : 'card-columns roo-card-uploader-container'
12813                 }
12814
12815             ]
12816         };
12817            
12818          
12819         return cfg;
12820     },
12821     
12822     getChildContainer : function() /// what children are added to.
12823     {
12824         return this.containerEl;
12825     },
12826    
12827     getButtonContainer : function() /// what children are added to.
12828     {
12829         return this.el.select(".roo-card-uploader-button-container").first();
12830     },
12831    
12832     initEvents : function()
12833     {
12834         
12835         Roo.bootstrap.Input.prototype.initEvents.call(this);
12836         
12837         var t = this;
12838         this.addxtype({
12839             xns: Roo.bootstrap,
12840
12841             xtype : 'Button',
12842             container_method : 'getButtonContainer' ,            
12843             html :  this.html, // fix changable?
12844             cls : 'w-100 ',
12845             listeners : {
12846                 'click' : function(btn, e) {
12847                     t.onClick(e);
12848                 }
12849             }
12850         });
12851         
12852         
12853         
12854         
12855         this.urlAPI = (window.createObjectURL && window) || 
12856                                 (window.URL && URL.revokeObjectURL && URL) || 
12857                                 (window.webkitURL && webkitURL);
12858                         
12859          
12860          
12861          
12862         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12863         
12864         this.selectorEl.on('change', this.onFileSelected, this);
12865         if (this.images) {
12866             var t = this;
12867             this.images.forEach(function(img) {
12868                 t.addCard(img)
12869             });
12870             this.images = false;
12871         }
12872         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12873          
12874        
12875     },
12876     
12877    
12878     onClick : function(e)
12879     {
12880         e.preventDefault();
12881          
12882         this.selectorEl.dom.click();
12883          
12884     },
12885     
12886     onFileSelected : function(e)
12887     {
12888         e.preventDefault();
12889         
12890         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12891             return;
12892         }
12893         
12894         Roo.each(this.selectorEl.dom.files, function(file){    
12895             this.addFile(file);
12896         }, this);
12897          
12898     },
12899     
12900       
12901     
12902       
12903     
12904     addFile : function(file)
12905     {
12906            
12907         if(typeof(file) === 'string'){
12908             throw "Add file by name?"; // should not happen
12909             return;
12910         }
12911         
12912         if(!file || !this.urlAPI){
12913             return;
12914         }
12915         
12916         // file;
12917         // file.type;
12918         
12919         var _this = this;
12920         
12921         
12922         var url = _this.urlAPI.createObjectURL( file);
12923            
12924         this.addCard({
12925             id : Roo.bootstrap.CardUploader.ID--,
12926             is_uploaded : false,
12927             src : url,
12928             srcfile : file,
12929             title : file.name,
12930             mimetype : file.type,
12931             preview : false,
12932             is_deleted : 0
12933         });
12934         
12935     },
12936     
12937     /**
12938      * addCard - add an Attachment to the uploader
12939      * @param data - the data about the image to upload
12940      *
12941      * {
12942           id : 123
12943           title : "Title of file",
12944           is_uploaded : false,
12945           src : "http://.....",
12946           srcfile : { the File upload object },
12947           mimetype : file.type,
12948           preview : false,
12949           is_deleted : 0
12950           .. any other data...
12951         }
12952      *
12953      * 
12954     */
12955     
12956     addCard : function (data)
12957     {
12958         // hidden input element?
12959         // if the file is not an image...
12960         //then we need to use something other that and header_image
12961         var t = this;
12962         //   remove.....
12963         var footer = [
12964             {
12965                 xns : Roo.bootstrap,
12966                 xtype : 'CardFooter',
12967                  items: [
12968                     {
12969                         xns : Roo.bootstrap,
12970                         xtype : 'Element',
12971                         cls : 'd-flex',
12972                         items : [
12973                             
12974                             {
12975                                 xns : Roo.bootstrap,
12976                                 xtype : 'Button',
12977                                 html : String.format("<small>{0}</small>", data.title),
12978                                 cls : 'col-10 text-left',
12979                                 size: 'sm',
12980                                 weight: 'link',
12981                                 fa : 'download',
12982                                 listeners : {
12983                                     click : function() {
12984                                      
12985                                         t.fireEvent( "download", t, data );
12986                                     }
12987                                 }
12988                             },
12989                           
12990                             {
12991                                 xns : Roo.bootstrap,
12992                                 xtype : 'Button',
12993                                 style: 'max-height: 28px; ',
12994                                 size : 'sm',
12995                                 weight: 'danger',
12996                                 cls : 'col-2',
12997                                 fa : 'times',
12998                                 listeners : {
12999                                     click : function() {
13000                                         t.removeCard(data.id)
13001                                     }
13002                                 }
13003                             }
13004                         ]
13005                     }
13006                     
13007                 ] 
13008             }
13009             
13010         ];
13011         
13012         var cn = this.addxtype(
13013             {
13014                  
13015                 xns : Roo.bootstrap,
13016                 xtype : 'Card',
13017                 closeable : true,
13018                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13019                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13020                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13021                 data : data,
13022                 html : false,
13023                  
13024                 items : footer,
13025                 initEvents : function() {
13026                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13027                     var card = this;
13028                     this.imgEl = this.el.select('.card-img-top').first();
13029                     if (this.imgEl) {
13030                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13031                         this.imgEl.set({ 'pointer' : 'cursor' });
13032                                   
13033                     }
13034                     this.getCardFooter().addClass('p-1');
13035                     
13036                   
13037                 }
13038                 
13039             }
13040         );
13041         // dont' really need ot update items.
13042         // this.items.push(cn);
13043         this.fileCollection.add(cn);
13044         
13045         if (!data.srcfile) {
13046             this.updateInput();
13047             return;
13048         }
13049             
13050         var _t = this;
13051         var reader = new FileReader();
13052         reader.addEventListener("load", function() {  
13053             data.srcdata =  reader.result;
13054             _t.updateInput();
13055         });
13056         reader.readAsDataURL(data.srcfile);
13057         
13058         
13059         
13060     },
13061     removeCard : function(id)
13062     {
13063         
13064         var card  = this.fileCollection.get(id);
13065         card.data.is_deleted = 1;
13066         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13067         //this.fileCollection.remove(card);
13068         //this.items = this.items.filter(function(e) { return e != card });
13069         // dont' really need ot update items.
13070         card.el.dom.parentNode.removeChild(card.el.dom);
13071         this.updateInput();
13072
13073         
13074     },
13075     reset: function()
13076     {
13077         this.fileCollection.each(function(card) {
13078             if (card.el.dom && card.el.dom.parentNode) {
13079                 card.el.dom.parentNode.removeChild(card.el.dom);
13080             }
13081         });
13082         this.fileCollection.clear();
13083         this.updateInput();
13084     },
13085     
13086     updateInput : function()
13087     {
13088          var data = [];
13089         this.fileCollection.each(function(e) {
13090             data.push(e.data);
13091             
13092         });
13093         this.inputEl().dom.value = JSON.stringify(data);
13094         
13095         
13096         
13097     }
13098     
13099     
13100 });
13101
13102
13103 Roo.bootstrap.CardUploader.ID = -1;/*
13104  * Based on:
13105  * Ext JS Library 1.1.1
13106  * Copyright(c) 2006-2007, Ext JS, LLC.
13107  *
13108  * Originally Released Under LGPL - original licence link has changed is not relivant.
13109  *
13110  * Fork - LGPL
13111  * <script type="text/javascript">
13112  */
13113
13114
13115 /**
13116  * @class Roo.data.SortTypes
13117  * @singleton
13118  * Defines the default sorting (casting?) comparison functions used when sorting data.
13119  */
13120 Roo.data.SortTypes = {
13121     /**
13122      * Default sort that does nothing
13123      * @param {Mixed} s The value being converted
13124      * @return {Mixed} The comparison value
13125      */
13126     none : function(s){
13127         return s;
13128     },
13129     
13130     /**
13131      * The regular expression used to strip tags
13132      * @type {RegExp}
13133      * @property
13134      */
13135     stripTagsRE : /<\/?[^>]+>/gi,
13136     
13137     /**
13138      * Strips all HTML tags to sort on text only
13139      * @param {Mixed} s The value being converted
13140      * @return {String} The comparison value
13141      */
13142     asText : function(s){
13143         return String(s).replace(this.stripTagsRE, "");
13144     },
13145     
13146     /**
13147      * Strips all HTML tags to sort on text only - Case insensitive
13148      * @param {Mixed} s The value being converted
13149      * @return {String} The comparison value
13150      */
13151     asUCText : function(s){
13152         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13153     },
13154     
13155     /**
13156      * Case insensitive string
13157      * @param {Mixed} s The value being converted
13158      * @return {String} The comparison value
13159      */
13160     asUCString : function(s) {
13161         return String(s).toUpperCase();
13162     },
13163     
13164     /**
13165      * Date sorting
13166      * @param {Mixed} s The value being converted
13167      * @return {Number} The comparison value
13168      */
13169     asDate : function(s) {
13170         if(!s){
13171             return 0;
13172         }
13173         if(s instanceof Date){
13174             return s.getTime();
13175         }
13176         return Date.parse(String(s));
13177     },
13178     
13179     /**
13180      * Float sorting
13181      * @param {Mixed} s The value being converted
13182      * @return {Float} The comparison value
13183      */
13184     asFloat : function(s) {
13185         var val = parseFloat(String(s).replace(/,/g, ""));
13186         if(isNaN(val)) {
13187             val = 0;
13188         }
13189         return val;
13190     },
13191     
13192     /**
13193      * Integer sorting
13194      * @param {Mixed} s The value being converted
13195      * @return {Number} The comparison value
13196      */
13197     asInt : function(s) {
13198         var val = parseInt(String(s).replace(/,/g, ""));
13199         if(isNaN(val)) {
13200             val = 0;
13201         }
13202         return val;
13203     }
13204 };/*
13205  * Based on:
13206  * Ext JS Library 1.1.1
13207  * Copyright(c) 2006-2007, Ext JS, LLC.
13208  *
13209  * Originally Released Under LGPL - original licence link has changed is not relivant.
13210  *
13211  * Fork - LGPL
13212  * <script type="text/javascript">
13213  */
13214
13215 /**
13216 * @class Roo.data.Record
13217  * Instances of this class encapsulate both record <em>definition</em> information, and record
13218  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13219  * to access Records cached in an {@link Roo.data.Store} object.<br>
13220  * <p>
13221  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13222  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13223  * objects.<br>
13224  * <p>
13225  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13226  * @constructor
13227  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13228  * {@link #create}. The parameters are the same.
13229  * @param {Array} data An associative Array of data values keyed by the field name.
13230  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13231  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13232  * not specified an integer id is generated.
13233  */
13234 Roo.data.Record = function(data, id){
13235     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13236     this.data = data;
13237 };
13238
13239 /**
13240  * Generate a constructor for a specific record layout.
13241  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13242  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13243  * Each field definition object may contain the following properties: <ul>
13244  * <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,
13245  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13246  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13247  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13248  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13249  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13250  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13251  * this may be omitted.</p></li>
13252  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13253  * <ul><li>auto (Default, implies no conversion)</li>
13254  * <li>string</li>
13255  * <li>int</li>
13256  * <li>float</li>
13257  * <li>boolean</li>
13258  * <li>date</li></ul></p></li>
13259  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13260  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13261  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13262  * by the Reader into an object that will be stored in the Record. It is passed the
13263  * following parameters:<ul>
13264  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13265  * </ul></p></li>
13266  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13267  * </ul>
13268  * <br>usage:<br><pre><code>
13269 var TopicRecord = Roo.data.Record.create(
13270     {name: 'title', mapping: 'topic_title'},
13271     {name: 'author', mapping: 'username'},
13272     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13273     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13274     {name: 'lastPoster', mapping: 'user2'},
13275     {name: 'excerpt', mapping: 'post_text'}
13276 );
13277
13278 var myNewRecord = new TopicRecord({
13279     title: 'Do my job please',
13280     author: 'noobie',
13281     totalPosts: 1,
13282     lastPost: new Date(),
13283     lastPoster: 'Animal',
13284     excerpt: 'No way dude!'
13285 });
13286 myStore.add(myNewRecord);
13287 </code></pre>
13288  * @method create
13289  * @static
13290  */
13291 Roo.data.Record.create = function(o){
13292     var f = function(){
13293         f.superclass.constructor.apply(this, arguments);
13294     };
13295     Roo.extend(f, Roo.data.Record);
13296     var p = f.prototype;
13297     p.fields = new Roo.util.MixedCollection(false, function(field){
13298         return field.name;
13299     });
13300     for(var i = 0, len = o.length; i < len; i++){
13301         p.fields.add(new Roo.data.Field(o[i]));
13302     }
13303     f.getField = function(name){
13304         return p.fields.get(name);  
13305     };
13306     return f;
13307 };
13308
13309 Roo.data.Record.AUTO_ID = 1000;
13310 Roo.data.Record.EDIT = 'edit';
13311 Roo.data.Record.REJECT = 'reject';
13312 Roo.data.Record.COMMIT = 'commit';
13313
13314 Roo.data.Record.prototype = {
13315     /**
13316      * Readonly flag - true if this record has been modified.
13317      * @type Boolean
13318      */
13319     dirty : false,
13320     editing : false,
13321     error: null,
13322     modified: null,
13323
13324     // private
13325     join : function(store){
13326         this.store = store;
13327     },
13328
13329     /**
13330      * Set the named field to the specified value.
13331      * @param {String} name The name of the field to set.
13332      * @param {Object} value The value to set the field to.
13333      */
13334     set : function(name, value){
13335         if(this.data[name] == value){
13336             return;
13337         }
13338         this.dirty = true;
13339         if(!this.modified){
13340             this.modified = {};
13341         }
13342         if(typeof this.modified[name] == 'undefined'){
13343             this.modified[name] = this.data[name];
13344         }
13345         this.data[name] = value;
13346         if(!this.editing && this.store){
13347             this.store.afterEdit(this);
13348         }       
13349     },
13350
13351     /**
13352      * Get the value of the named field.
13353      * @param {String} name The name of the field to get the value of.
13354      * @return {Object} The value of the field.
13355      */
13356     get : function(name){
13357         return this.data[name]; 
13358     },
13359
13360     // private
13361     beginEdit : function(){
13362         this.editing = true;
13363         this.modified = {}; 
13364     },
13365
13366     // private
13367     cancelEdit : function(){
13368         this.editing = false;
13369         delete this.modified;
13370     },
13371
13372     // private
13373     endEdit : function(){
13374         this.editing = false;
13375         if(this.dirty && this.store){
13376             this.store.afterEdit(this);
13377         }
13378     },
13379
13380     /**
13381      * Usually called by the {@link Roo.data.Store} which owns the Record.
13382      * Rejects all changes made to the Record since either creation, or the last commit operation.
13383      * Modified fields are reverted to their original values.
13384      * <p>
13385      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13386      * of reject operations.
13387      */
13388     reject : function(){
13389         var m = this.modified;
13390         for(var n in m){
13391             if(typeof m[n] != "function"){
13392                 this.data[n] = m[n];
13393             }
13394         }
13395         this.dirty = false;
13396         delete this.modified;
13397         this.editing = false;
13398         if(this.store){
13399             this.store.afterReject(this);
13400         }
13401     },
13402
13403     /**
13404      * Usually called by the {@link Roo.data.Store} which owns the Record.
13405      * Commits all changes made to the Record since either creation, or the last commit operation.
13406      * <p>
13407      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13408      * of commit operations.
13409      */
13410     commit : function(){
13411         this.dirty = false;
13412         delete this.modified;
13413         this.editing = false;
13414         if(this.store){
13415             this.store.afterCommit(this);
13416         }
13417     },
13418
13419     // private
13420     hasError : function(){
13421         return this.error != null;
13422     },
13423
13424     // private
13425     clearError : function(){
13426         this.error = null;
13427     },
13428
13429     /**
13430      * Creates a copy of this record.
13431      * @param {String} id (optional) A new record id if you don't want to use this record's id
13432      * @return {Record}
13433      */
13434     copy : function(newId) {
13435         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13436     }
13437 };/*
13438  * Based on:
13439  * Ext JS Library 1.1.1
13440  * Copyright(c) 2006-2007, Ext JS, LLC.
13441  *
13442  * Originally Released Under LGPL - original licence link has changed is not relivant.
13443  *
13444  * Fork - LGPL
13445  * <script type="text/javascript">
13446  */
13447
13448
13449
13450 /**
13451  * @class Roo.data.Store
13452  * @extends Roo.util.Observable
13453  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13454  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13455  * <p>
13456  * 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
13457  * has no knowledge of the format of the data returned by the Proxy.<br>
13458  * <p>
13459  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13460  * instances from the data object. These records are cached and made available through accessor functions.
13461  * @constructor
13462  * Creates a new Store.
13463  * @param {Object} config A config object containing the objects needed for the Store to access data,
13464  * and read the data into Records.
13465  */
13466 Roo.data.Store = function(config){
13467     this.data = new Roo.util.MixedCollection(false);
13468     this.data.getKey = function(o){
13469         return o.id;
13470     };
13471     this.baseParams = {};
13472     // private
13473     this.paramNames = {
13474         "start" : "start",
13475         "limit" : "limit",
13476         "sort" : "sort",
13477         "dir" : "dir",
13478         "multisort" : "_multisort"
13479     };
13480
13481     if(config && config.data){
13482         this.inlineData = config.data;
13483         delete config.data;
13484     }
13485
13486     Roo.apply(this, config);
13487     
13488     if(this.reader){ // reader passed
13489         this.reader = Roo.factory(this.reader, Roo.data);
13490         this.reader.xmodule = this.xmodule || false;
13491         if(!this.recordType){
13492             this.recordType = this.reader.recordType;
13493         }
13494         if(this.reader.onMetaChange){
13495             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13496         }
13497     }
13498
13499     if(this.recordType){
13500         this.fields = this.recordType.prototype.fields;
13501     }
13502     this.modified = [];
13503
13504     this.addEvents({
13505         /**
13506          * @event datachanged
13507          * Fires when the data cache has changed, and a widget which is using this Store
13508          * as a Record cache should refresh its view.
13509          * @param {Store} this
13510          */
13511         datachanged : true,
13512         /**
13513          * @event metachange
13514          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13515          * @param {Store} this
13516          * @param {Object} meta The JSON metadata
13517          */
13518         metachange : true,
13519         /**
13520          * @event add
13521          * Fires when Records have been added to the Store
13522          * @param {Store} this
13523          * @param {Roo.data.Record[]} records The array of Records added
13524          * @param {Number} index The index at which the record(s) were added
13525          */
13526         add : true,
13527         /**
13528          * @event remove
13529          * Fires when a Record has been removed from the Store
13530          * @param {Store} this
13531          * @param {Roo.data.Record} record The Record that was removed
13532          * @param {Number} index The index at which the record was removed
13533          */
13534         remove : true,
13535         /**
13536          * @event update
13537          * Fires when a Record has been updated
13538          * @param {Store} this
13539          * @param {Roo.data.Record} record The Record that was updated
13540          * @param {String} operation The update operation being performed.  Value may be one of:
13541          * <pre><code>
13542  Roo.data.Record.EDIT
13543  Roo.data.Record.REJECT
13544  Roo.data.Record.COMMIT
13545          * </code></pre>
13546          */
13547         update : true,
13548         /**
13549          * @event clear
13550          * Fires when the data cache has been cleared.
13551          * @param {Store} this
13552          */
13553         clear : true,
13554         /**
13555          * @event beforeload
13556          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13557          * the load action will be canceled.
13558          * @param {Store} this
13559          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13560          */
13561         beforeload : true,
13562         /**
13563          * @event beforeloadadd
13564          * Fires after a new set of Records has been loaded.
13565          * @param {Store} this
13566          * @param {Roo.data.Record[]} records The Records that were loaded
13567          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13568          */
13569         beforeloadadd : true,
13570         /**
13571          * @event load
13572          * Fires after a new set of Records has been loaded, before they are added to the store.
13573          * @param {Store} this
13574          * @param {Roo.data.Record[]} records The Records that were loaded
13575          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13576          * @params {Object} return from reader
13577          */
13578         load : true,
13579         /**
13580          * @event loadexception
13581          * Fires if an exception occurs in the Proxy during loading.
13582          * Called with the signature of the Proxy's "loadexception" event.
13583          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13584          * 
13585          * @param {Proxy} 
13586          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13587          * @param {Object} load options 
13588          * @param {Object} jsonData from your request (normally this contains the Exception)
13589          */
13590         loadexception : true
13591     });
13592     
13593     if(this.proxy){
13594         this.proxy = Roo.factory(this.proxy, Roo.data);
13595         this.proxy.xmodule = this.xmodule || false;
13596         this.relayEvents(this.proxy,  ["loadexception"]);
13597     }
13598     this.sortToggle = {};
13599     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13600
13601     Roo.data.Store.superclass.constructor.call(this);
13602
13603     if(this.inlineData){
13604         this.loadData(this.inlineData);
13605         delete this.inlineData;
13606     }
13607 };
13608
13609 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13610      /**
13611     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13612     * without a remote query - used by combo/forms at present.
13613     */
13614     
13615     /**
13616     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13617     */
13618     /**
13619     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13620     */
13621     /**
13622     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13623     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13624     */
13625     /**
13626     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13627     * on any HTTP request
13628     */
13629     /**
13630     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13631     */
13632     /**
13633     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13634     */
13635     multiSort: false,
13636     /**
13637     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13638     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13639     */
13640     remoteSort : false,
13641
13642     /**
13643     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13644      * loaded or when a record is removed. (defaults to false).
13645     */
13646     pruneModifiedRecords : false,
13647
13648     // private
13649     lastOptions : null,
13650
13651     /**
13652      * Add Records to the Store and fires the add event.
13653      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13654      */
13655     add : function(records){
13656         records = [].concat(records);
13657         for(var i = 0, len = records.length; i < len; i++){
13658             records[i].join(this);
13659         }
13660         var index = this.data.length;
13661         this.data.addAll(records);
13662         this.fireEvent("add", this, records, index);
13663     },
13664
13665     /**
13666      * Remove a Record from the Store and fires the remove event.
13667      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13668      */
13669     remove : function(record){
13670         var index = this.data.indexOf(record);
13671         this.data.removeAt(index);
13672  
13673         if(this.pruneModifiedRecords){
13674             this.modified.remove(record);
13675         }
13676         this.fireEvent("remove", this, record, index);
13677     },
13678
13679     /**
13680      * Remove all Records from the Store and fires the clear event.
13681      */
13682     removeAll : function(){
13683         this.data.clear();
13684         if(this.pruneModifiedRecords){
13685             this.modified = [];
13686         }
13687         this.fireEvent("clear", this);
13688     },
13689
13690     /**
13691      * Inserts Records to the Store at the given index and fires the add event.
13692      * @param {Number} index The start index at which to insert the passed Records.
13693      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13694      */
13695     insert : function(index, records){
13696         records = [].concat(records);
13697         for(var i = 0, len = records.length; i < len; i++){
13698             this.data.insert(index, records[i]);
13699             records[i].join(this);
13700         }
13701         this.fireEvent("add", this, records, index);
13702     },
13703
13704     /**
13705      * Get the index within the cache of the passed Record.
13706      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13707      * @return {Number} The index of the passed Record. Returns -1 if not found.
13708      */
13709     indexOf : function(record){
13710         return this.data.indexOf(record);
13711     },
13712
13713     /**
13714      * Get the index within the cache of the Record with the passed id.
13715      * @param {String} id The id of the Record to find.
13716      * @return {Number} The index of the Record. Returns -1 if not found.
13717      */
13718     indexOfId : function(id){
13719         return this.data.indexOfKey(id);
13720     },
13721
13722     /**
13723      * Get the Record with the specified id.
13724      * @param {String} id The id of the Record to find.
13725      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13726      */
13727     getById : function(id){
13728         return this.data.key(id);
13729     },
13730
13731     /**
13732      * Get the Record at the specified index.
13733      * @param {Number} index The index of the Record to find.
13734      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13735      */
13736     getAt : function(index){
13737         return this.data.itemAt(index);
13738     },
13739
13740     /**
13741      * Returns a range of Records between specified indices.
13742      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13743      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13744      * @return {Roo.data.Record[]} An array of Records
13745      */
13746     getRange : function(start, end){
13747         return this.data.getRange(start, end);
13748     },
13749
13750     // private
13751     storeOptions : function(o){
13752         o = Roo.apply({}, o);
13753         delete o.callback;
13754         delete o.scope;
13755         this.lastOptions = o;
13756     },
13757
13758     /**
13759      * Loads the Record cache from the configured Proxy using the configured Reader.
13760      * <p>
13761      * If using remote paging, then the first load call must specify the <em>start</em>
13762      * and <em>limit</em> properties in the options.params property to establish the initial
13763      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13764      * <p>
13765      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13766      * and this call will return before the new data has been loaded. Perform any post-processing
13767      * in a callback function, or in a "load" event handler.</strong>
13768      * <p>
13769      * @param {Object} options An object containing properties which control loading options:<ul>
13770      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13771      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13772      * passed the following arguments:<ul>
13773      * <li>r : Roo.data.Record[]</li>
13774      * <li>options: Options object from the load call</li>
13775      * <li>success: Boolean success indicator</li></ul></li>
13776      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13777      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13778      * </ul>
13779      */
13780     load : function(options){
13781         options = options || {};
13782         if(this.fireEvent("beforeload", this, options) !== false){
13783             this.storeOptions(options);
13784             var p = Roo.apply(options.params || {}, this.baseParams);
13785             // if meta was not loaded from remote source.. try requesting it.
13786             if (!this.reader.metaFromRemote) {
13787                 p._requestMeta = 1;
13788             }
13789             if(this.sortInfo && this.remoteSort){
13790                 var pn = this.paramNames;
13791                 p[pn["sort"]] = this.sortInfo.field;
13792                 p[pn["dir"]] = this.sortInfo.direction;
13793             }
13794             if (this.multiSort) {
13795                 var pn = this.paramNames;
13796                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13797             }
13798             
13799             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13800         }
13801     },
13802
13803     /**
13804      * Reloads the Record cache from the configured Proxy using the configured Reader and
13805      * the options from the last load operation performed.
13806      * @param {Object} options (optional) An object containing properties which may override the options
13807      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13808      * the most recently used options are reused).
13809      */
13810     reload : function(options){
13811         this.load(Roo.applyIf(options||{}, this.lastOptions));
13812     },
13813
13814     // private
13815     // Called as a callback by the Reader during a load operation.
13816     loadRecords : function(o, options, success){
13817         if(!o || success === false){
13818             if(success !== false){
13819                 this.fireEvent("load", this, [], options, o);
13820             }
13821             if(options.callback){
13822                 options.callback.call(options.scope || this, [], options, false);
13823             }
13824             return;
13825         }
13826         // if data returned failure - throw an exception.
13827         if (o.success === false) {
13828             // show a message if no listener is registered.
13829             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13830                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13831             }
13832             // loadmask wil be hooked into this..
13833             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13834             return;
13835         }
13836         var r = o.records, t = o.totalRecords || r.length;
13837         
13838         this.fireEvent("beforeloadadd", this, r, options, o);
13839         
13840         if(!options || options.add !== true){
13841             if(this.pruneModifiedRecords){
13842                 this.modified = [];
13843             }
13844             for(var i = 0, len = r.length; i < len; i++){
13845                 r[i].join(this);
13846             }
13847             if(this.snapshot){
13848                 this.data = this.snapshot;
13849                 delete this.snapshot;
13850             }
13851             this.data.clear();
13852             this.data.addAll(r);
13853             this.totalLength = t;
13854             this.applySort();
13855             this.fireEvent("datachanged", this);
13856         }else{
13857             this.totalLength = Math.max(t, this.data.length+r.length);
13858             this.add(r);
13859         }
13860         
13861         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13862                 
13863             var e = new Roo.data.Record({});
13864
13865             e.set(this.parent.displayField, this.parent.emptyTitle);
13866             e.set(this.parent.valueField, '');
13867
13868             this.insert(0, e);
13869         }
13870             
13871         this.fireEvent("load", this, r, options, o);
13872         if(options.callback){
13873             options.callback.call(options.scope || this, r, options, true);
13874         }
13875     },
13876
13877
13878     /**
13879      * Loads data from a passed data block. A Reader which understands the format of the data
13880      * must have been configured in the constructor.
13881      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13882      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13883      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13884      */
13885     loadData : function(o, append){
13886         var r = this.reader.readRecords(o);
13887         this.loadRecords(r, {add: append}, true);
13888     },
13889     
13890      /**
13891      * using 'cn' the nested child reader read the child array into it's child stores.
13892      * @param {Object} rec The record with a 'children array
13893      */
13894     loadDataFromChildren : function(rec)
13895     {
13896         this.loadData(this.reader.toLoadData(rec));
13897     },
13898     
13899
13900     /**
13901      * Gets the number of cached records.
13902      * <p>
13903      * <em>If using paging, this may not be the total size of the dataset. If the data object
13904      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13905      * the data set size</em>
13906      */
13907     getCount : function(){
13908         return this.data.length || 0;
13909     },
13910
13911     /**
13912      * Gets the total number of records in the dataset as returned by the server.
13913      * <p>
13914      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13915      * the dataset size</em>
13916      */
13917     getTotalCount : function(){
13918         return this.totalLength || 0;
13919     },
13920
13921     /**
13922      * Returns the sort state of the Store as an object with two properties:
13923      * <pre><code>
13924  field {String} The name of the field by which the Records are sorted
13925  direction {String} The sort order, "ASC" or "DESC"
13926      * </code></pre>
13927      */
13928     getSortState : function(){
13929         return this.sortInfo;
13930     },
13931
13932     // private
13933     applySort : function(){
13934         if(this.sortInfo && !this.remoteSort){
13935             var s = this.sortInfo, f = s.field;
13936             var st = this.fields.get(f).sortType;
13937             var fn = function(r1, r2){
13938                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13939                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13940             };
13941             this.data.sort(s.direction, fn);
13942             if(this.snapshot && this.snapshot != this.data){
13943                 this.snapshot.sort(s.direction, fn);
13944             }
13945         }
13946     },
13947
13948     /**
13949      * Sets the default sort column and order to be used by the next load operation.
13950      * @param {String} fieldName The name of the field to sort by.
13951      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13952      */
13953     setDefaultSort : function(field, dir){
13954         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13955     },
13956
13957     /**
13958      * Sort the Records.
13959      * If remote sorting is used, the sort is performed on the server, and the cache is
13960      * reloaded. If local sorting is used, the cache is sorted internally.
13961      * @param {String} fieldName The name of the field to sort by.
13962      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13963      */
13964     sort : function(fieldName, dir){
13965         var f = this.fields.get(fieldName);
13966         if(!dir){
13967             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13968             
13969             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13970                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13971             }else{
13972                 dir = f.sortDir;
13973             }
13974         }
13975         this.sortToggle[f.name] = dir;
13976         this.sortInfo = {field: f.name, direction: dir};
13977         if(!this.remoteSort){
13978             this.applySort();
13979             this.fireEvent("datachanged", this);
13980         }else{
13981             this.load(this.lastOptions);
13982         }
13983     },
13984
13985     /**
13986      * Calls the specified function for each of the Records in the cache.
13987      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13988      * Returning <em>false</em> aborts and exits the iteration.
13989      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13990      */
13991     each : function(fn, scope){
13992         this.data.each(fn, scope);
13993     },
13994
13995     /**
13996      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13997      * (e.g., during paging).
13998      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13999      */
14000     getModifiedRecords : function(){
14001         return this.modified;
14002     },
14003
14004     // private
14005     createFilterFn : function(property, value, anyMatch){
14006         if(!value.exec){ // not a regex
14007             value = String(value);
14008             if(value.length == 0){
14009                 return false;
14010             }
14011             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14012         }
14013         return function(r){
14014             return value.test(r.data[property]);
14015         };
14016     },
14017
14018     /**
14019      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14020      * @param {String} property A field on your records
14021      * @param {Number} start The record index to start at (defaults to 0)
14022      * @param {Number} end The last record index to include (defaults to length - 1)
14023      * @return {Number} The sum
14024      */
14025     sum : function(property, start, end){
14026         var rs = this.data.items, v = 0;
14027         start = start || 0;
14028         end = (end || end === 0) ? end : rs.length-1;
14029
14030         for(var i = start; i <= end; i++){
14031             v += (rs[i].data[property] || 0);
14032         }
14033         return v;
14034     },
14035
14036     /**
14037      * Filter the records by a specified property.
14038      * @param {String} field A field on your records
14039      * @param {String/RegExp} value Either a string that the field
14040      * should start with or a RegExp to test against the field
14041      * @param {Boolean} anyMatch True to match any part not just the beginning
14042      */
14043     filter : function(property, value, anyMatch){
14044         var fn = this.createFilterFn(property, value, anyMatch);
14045         return fn ? this.filterBy(fn) : this.clearFilter();
14046     },
14047
14048     /**
14049      * Filter by a function. The specified function will be called with each
14050      * record in this data source. If the function returns true the record is included,
14051      * otherwise it is filtered.
14052      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14053      * @param {Object} scope (optional) The scope of the function (defaults to this)
14054      */
14055     filterBy : function(fn, scope){
14056         this.snapshot = this.snapshot || this.data;
14057         this.data = this.queryBy(fn, scope||this);
14058         this.fireEvent("datachanged", this);
14059     },
14060
14061     /**
14062      * Query the records by a specified property.
14063      * @param {String} field A field on your records
14064      * @param {String/RegExp} value Either a string that the field
14065      * should start with or a RegExp to test against the field
14066      * @param {Boolean} anyMatch True to match any part not just the beginning
14067      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14068      */
14069     query : function(property, value, anyMatch){
14070         var fn = this.createFilterFn(property, value, anyMatch);
14071         return fn ? this.queryBy(fn) : this.data.clone();
14072     },
14073
14074     /**
14075      * Query by a function. The specified function will be called with each
14076      * record in this data source. If the function returns true the record is included
14077      * in the results.
14078      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14079      * @param {Object} scope (optional) The scope of the function (defaults to this)
14080       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14081      **/
14082     queryBy : function(fn, scope){
14083         var data = this.snapshot || this.data;
14084         return data.filterBy(fn, scope||this);
14085     },
14086
14087     /**
14088      * Collects unique values for a particular dataIndex from this store.
14089      * @param {String} dataIndex The property to collect
14090      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14091      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14092      * @return {Array} An array of the unique values
14093      **/
14094     collect : function(dataIndex, allowNull, bypassFilter){
14095         var d = (bypassFilter === true && this.snapshot) ?
14096                 this.snapshot.items : this.data.items;
14097         var v, sv, r = [], l = {};
14098         for(var i = 0, len = d.length; i < len; i++){
14099             v = d[i].data[dataIndex];
14100             sv = String(v);
14101             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14102                 l[sv] = true;
14103                 r[r.length] = v;
14104             }
14105         }
14106         return r;
14107     },
14108
14109     /**
14110      * Revert to a view of the Record cache with no filtering applied.
14111      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14112      */
14113     clearFilter : function(suppressEvent){
14114         if(this.snapshot && this.snapshot != this.data){
14115             this.data = this.snapshot;
14116             delete this.snapshot;
14117             if(suppressEvent !== true){
14118                 this.fireEvent("datachanged", this);
14119             }
14120         }
14121     },
14122
14123     // private
14124     afterEdit : function(record){
14125         if(this.modified.indexOf(record) == -1){
14126             this.modified.push(record);
14127         }
14128         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14129     },
14130     
14131     // private
14132     afterReject : function(record){
14133         this.modified.remove(record);
14134         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14135     },
14136
14137     // private
14138     afterCommit : function(record){
14139         this.modified.remove(record);
14140         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14141     },
14142
14143     /**
14144      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14145      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14146      */
14147     commitChanges : function(){
14148         var m = this.modified.slice(0);
14149         this.modified = [];
14150         for(var i = 0, len = m.length; i < len; i++){
14151             m[i].commit();
14152         }
14153     },
14154
14155     /**
14156      * Cancel outstanding changes on all changed records.
14157      */
14158     rejectChanges : function(){
14159         var m = this.modified.slice(0);
14160         this.modified = [];
14161         for(var i = 0, len = m.length; i < len; i++){
14162             m[i].reject();
14163         }
14164     },
14165
14166     onMetaChange : function(meta, rtype, o){
14167         this.recordType = rtype;
14168         this.fields = rtype.prototype.fields;
14169         delete this.snapshot;
14170         this.sortInfo = meta.sortInfo || this.sortInfo;
14171         this.modified = [];
14172         this.fireEvent('metachange', this, this.reader.meta);
14173     },
14174     
14175     moveIndex : function(data, type)
14176     {
14177         var index = this.indexOf(data);
14178         
14179         var newIndex = index + type;
14180         
14181         this.remove(data);
14182         
14183         this.insert(newIndex, data);
14184         
14185     }
14186 });/*
14187  * Based on:
14188  * Ext JS Library 1.1.1
14189  * Copyright(c) 2006-2007, Ext JS, LLC.
14190  *
14191  * Originally Released Under LGPL - original licence link has changed is not relivant.
14192  *
14193  * Fork - LGPL
14194  * <script type="text/javascript">
14195  */
14196
14197 /**
14198  * @class Roo.data.SimpleStore
14199  * @extends Roo.data.Store
14200  * Small helper class to make creating Stores from Array data easier.
14201  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14202  * @cfg {Array} fields An array of field definition objects, or field name strings.
14203  * @cfg {Object} an existing reader (eg. copied from another store)
14204  * @cfg {Array} data The multi-dimensional array of data
14205  * @constructor
14206  * @param {Object} config
14207  */
14208 Roo.data.SimpleStore = function(config)
14209 {
14210     Roo.data.SimpleStore.superclass.constructor.call(this, {
14211         isLocal : true,
14212         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14213                 id: config.id
14214             },
14215             Roo.data.Record.create(config.fields)
14216         ),
14217         proxy : new Roo.data.MemoryProxy(config.data)
14218     });
14219     this.load();
14220 };
14221 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14222  * Based on:
14223  * Ext JS Library 1.1.1
14224  * Copyright(c) 2006-2007, Ext JS, LLC.
14225  *
14226  * Originally Released Under LGPL - original licence link has changed is not relivant.
14227  *
14228  * Fork - LGPL
14229  * <script type="text/javascript">
14230  */
14231
14232 /**
14233 /**
14234  * @extends Roo.data.Store
14235  * @class Roo.data.JsonStore
14236  * Small helper class to make creating Stores for JSON data easier. <br/>
14237 <pre><code>
14238 var store = new Roo.data.JsonStore({
14239     url: 'get-images.php',
14240     root: 'images',
14241     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14242 });
14243 </code></pre>
14244  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14245  * JsonReader and HttpProxy (unless inline data is provided).</b>
14246  * @cfg {Array} fields An array of field definition objects, or field name strings.
14247  * @constructor
14248  * @param {Object} config
14249  */
14250 Roo.data.JsonStore = function(c){
14251     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14252         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14253         reader: new Roo.data.JsonReader(c, c.fields)
14254     }));
14255 };
14256 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14257  * Based on:
14258  * Ext JS Library 1.1.1
14259  * Copyright(c) 2006-2007, Ext JS, LLC.
14260  *
14261  * Originally Released Under LGPL - original licence link has changed is not relivant.
14262  *
14263  * Fork - LGPL
14264  * <script type="text/javascript">
14265  */
14266
14267  
14268 Roo.data.Field = function(config){
14269     if(typeof config == "string"){
14270         config = {name: config};
14271     }
14272     Roo.apply(this, config);
14273     
14274     if(!this.type){
14275         this.type = "auto";
14276     }
14277     
14278     var st = Roo.data.SortTypes;
14279     // named sortTypes are supported, here we look them up
14280     if(typeof this.sortType == "string"){
14281         this.sortType = st[this.sortType];
14282     }
14283     
14284     // set default sortType for strings and dates
14285     if(!this.sortType){
14286         switch(this.type){
14287             case "string":
14288                 this.sortType = st.asUCString;
14289                 break;
14290             case "date":
14291                 this.sortType = st.asDate;
14292                 break;
14293             default:
14294                 this.sortType = st.none;
14295         }
14296     }
14297
14298     // define once
14299     var stripRe = /[\$,%]/g;
14300
14301     // prebuilt conversion function for this field, instead of
14302     // switching every time we're reading a value
14303     if(!this.convert){
14304         var cv, dateFormat = this.dateFormat;
14305         switch(this.type){
14306             case "":
14307             case "auto":
14308             case undefined:
14309                 cv = function(v){ return v; };
14310                 break;
14311             case "string":
14312                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14313                 break;
14314             case "int":
14315                 cv = function(v){
14316                     return v !== undefined && v !== null && v !== '' ?
14317                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14318                     };
14319                 break;
14320             case "float":
14321                 cv = function(v){
14322                     return v !== undefined && v !== null && v !== '' ?
14323                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14324                     };
14325                 break;
14326             case "bool":
14327             case "boolean":
14328                 cv = function(v){ return v === true || v === "true" || v == 1; };
14329                 break;
14330             case "date":
14331                 cv = function(v){
14332                     if(!v){
14333                         return '';
14334                     }
14335                     if(v instanceof Date){
14336                         return v;
14337                     }
14338                     if(dateFormat){
14339                         if(dateFormat == "timestamp"){
14340                             return new Date(v*1000);
14341                         }
14342                         return Date.parseDate(v, dateFormat);
14343                     }
14344                     var parsed = Date.parse(v);
14345                     return parsed ? new Date(parsed) : null;
14346                 };
14347              break;
14348             
14349         }
14350         this.convert = cv;
14351     }
14352 };
14353
14354 Roo.data.Field.prototype = {
14355     dateFormat: null,
14356     defaultValue: "",
14357     mapping: null,
14358     sortType : null,
14359     sortDir : "ASC"
14360 };/*
14361  * Based on:
14362  * Ext JS Library 1.1.1
14363  * Copyright(c) 2006-2007, Ext JS, LLC.
14364  *
14365  * Originally Released Under LGPL - original licence link has changed is not relivant.
14366  *
14367  * Fork - LGPL
14368  * <script type="text/javascript">
14369  */
14370  
14371 // Base class for reading structured data from a data source.  This class is intended to be
14372 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14373
14374 /**
14375  * @class Roo.data.DataReader
14376  * Base class for reading structured data from a data source.  This class is intended to be
14377  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14378  */
14379
14380 Roo.data.DataReader = function(meta, recordType){
14381     
14382     this.meta = meta;
14383     
14384     this.recordType = recordType instanceof Array ? 
14385         Roo.data.Record.create(recordType) : recordType;
14386 };
14387
14388 Roo.data.DataReader.prototype = {
14389     
14390     
14391     readerType : 'Data',
14392      /**
14393      * Create an empty record
14394      * @param {Object} data (optional) - overlay some values
14395      * @return {Roo.data.Record} record created.
14396      */
14397     newRow :  function(d) {
14398         var da =  {};
14399         this.recordType.prototype.fields.each(function(c) {
14400             switch( c.type) {
14401                 case 'int' : da[c.name] = 0; break;
14402                 case 'date' : da[c.name] = new Date(); break;
14403                 case 'float' : da[c.name] = 0.0; break;
14404                 case 'boolean' : da[c.name] = false; break;
14405                 default : da[c.name] = ""; break;
14406             }
14407             
14408         });
14409         return new this.recordType(Roo.apply(da, d));
14410     }
14411     
14412     
14413 };/*
14414  * Based on:
14415  * Ext JS Library 1.1.1
14416  * Copyright(c) 2006-2007, Ext JS, LLC.
14417  *
14418  * Originally Released Under LGPL - original licence link has changed is not relivant.
14419  *
14420  * Fork - LGPL
14421  * <script type="text/javascript">
14422  */
14423
14424 /**
14425  * @class Roo.data.DataProxy
14426  * @extends Roo.data.Observable
14427  * This class is an abstract base class for implementations which provide retrieval of
14428  * unformatted data objects.<br>
14429  * <p>
14430  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14431  * (of the appropriate type which knows how to parse the data object) to provide a block of
14432  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14433  * <p>
14434  * Custom implementations must implement the load method as described in
14435  * {@link Roo.data.HttpProxy#load}.
14436  */
14437 Roo.data.DataProxy = function(){
14438     this.addEvents({
14439         /**
14440          * @event beforeload
14441          * Fires before a network request is made to retrieve a data object.
14442          * @param {Object} This DataProxy object.
14443          * @param {Object} params The params parameter to the load function.
14444          */
14445         beforeload : true,
14446         /**
14447          * @event load
14448          * Fires before the load method's callback is called.
14449          * @param {Object} This DataProxy object.
14450          * @param {Object} o The data object.
14451          * @param {Object} arg The callback argument object passed to the load function.
14452          */
14453         load : true,
14454         /**
14455          * @event loadexception
14456          * Fires if an Exception occurs during data retrieval.
14457          * @param {Object} This DataProxy object.
14458          * @param {Object} o The data object.
14459          * @param {Object} arg The callback argument object passed to the load function.
14460          * @param {Object} e The Exception.
14461          */
14462         loadexception : true
14463     });
14464     Roo.data.DataProxy.superclass.constructor.call(this);
14465 };
14466
14467 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14468
14469     /**
14470      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14471      */
14472 /*
14473  * Based on:
14474  * Ext JS Library 1.1.1
14475  * Copyright(c) 2006-2007, Ext JS, LLC.
14476  *
14477  * Originally Released Under LGPL - original licence link has changed is not relivant.
14478  *
14479  * Fork - LGPL
14480  * <script type="text/javascript">
14481  */
14482 /**
14483  * @class Roo.data.MemoryProxy
14484  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14485  * to the Reader when its load method is called.
14486  * @constructor
14487  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14488  */
14489 Roo.data.MemoryProxy = function(data){
14490     if (data.data) {
14491         data = data.data;
14492     }
14493     Roo.data.MemoryProxy.superclass.constructor.call(this);
14494     this.data = data;
14495 };
14496
14497 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14498     
14499     /**
14500      * Load data from the requested source (in this case an in-memory
14501      * data object passed to the constructor), read the data object into
14502      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14503      * process that block using the passed callback.
14504      * @param {Object} params This parameter is not used by the MemoryProxy class.
14505      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14506      * object into a block of Roo.data.Records.
14507      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14508      * The function must be passed <ul>
14509      * <li>The Record block object</li>
14510      * <li>The "arg" argument from the load function</li>
14511      * <li>A boolean success indicator</li>
14512      * </ul>
14513      * @param {Object} scope The scope in which to call the callback
14514      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14515      */
14516     load : function(params, reader, callback, scope, arg){
14517         params = params || {};
14518         var result;
14519         try {
14520             result = reader.readRecords(params.data ? params.data :this.data);
14521         }catch(e){
14522             this.fireEvent("loadexception", this, arg, null, e);
14523             callback.call(scope, null, arg, false);
14524             return;
14525         }
14526         callback.call(scope, result, arg, true);
14527     },
14528     
14529     // private
14530     update : function(params, records){
14531         
14532     }
14533 });/*
14534  * Based on:
14535  * Ext JS Library 1.1.1
14536  * Copyright(c) 2006-2007, Ext JS, LLC.
14537  *
14538  * Originally Released Under LGPL - original licence link has changed is not relivant.
14539  *
14540  * Fork - LGPL
14541  * <script type="text/javascript">
14542  */
14543 /**
14544  * @class Roo.data.HttpProxy
14545  * @extends Roo.data.DataProxy
14546  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14547  * configured to reference a certain URL.<br><br>
14548  * <p>
14549  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14550  * from which the running page was served.<br><br>
14551  * <p>
14552  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14553  * <p>
14554  * Be aware that to enable the browser to parse an XML document, the server must set
14555  * the Content-Type header in the HTTP response to "text/xml".
14556  * @constructor
14557  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14558  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14559  * will be used to make the request.
14560  */
14561 Roo.data.HttpProxy = function(conn){
14562     Roo.data.HttpProxy.superclass.constructor.call(this);
14563     // is conn a conn config or a real conn?
14564     this.conn = conn;
14565     this.useAjax = !conn || !conn.events;
14566   
14567 };
14568
14569 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14570     // thse are take from connection...
14571     
14572     /**
14573      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14574      */
14575     /**
14576      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14577      * extra parameters to each request made by this object. (defaults to undefined)
14578      */
14579     /**
14580      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14581      *  to each request made by this object. (defaults to undefined)
14582      */
14583     /**
14584      * @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)
14585      */
14586     /**
14587      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14588      */
14589      /**
14590      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14591      * @type Boolean
14592      */
14593   
14594
14595     /**
14596      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14597      * @type Boolean
14598      */
14599     /**
14600      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14601      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14602      * a finer-grained basis than the DataProxy events.
14603      */
14604     getConnection : function(){
14605         return this.useAjax ? Roo.Ajax : this.conn;
14606     },
14607
14608     /**
14609      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14610      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14611      * process that block using the passed callback.
14612      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14613      * for the request to the remote server.
14614      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14615      * object into a block of Roo.data.Records.
14616      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14617      * The function must be passed <ul>
14618      * <li>The Record block object</li>
14619      * <li>The "arg" argument from the load function</li>
14620      * <li>A boolean success indicator</li>
14621      * </ul>
14622      * @param {Object} scope The scope in which to call the callback
14623      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14624      */
14625     load : function(params, reader, callback, scope, arg){
14626         if(this.fireEvent("beforeload", this, params) !== false){
14627             var  o = {
14628                 params : params || {},
14629                 request: {
14630                     callback : callback,
14631                     scope : scope,
14632                     arg : arg
14633                 },
14634                 reader: reader,
14635                 callback : this.loadResponse,
14636                 scope: this
14637             };
14638             if(this.useAjax){
14639                 Roo.applyIf(o, this.conn);
14640                 if(this.activeRequest){
14641                     Roo.Ajax.abort(this.activeRequest);
14642                 }
14643                 this.activeRequest = Roo.Ajax.request(o);
14644             }else{
14645                 this.conn.request(o);
14646             }
14647         }else{
14648             callback.call(scope||this, null, arg, false);
14649         }
14650     },
14651
14652     // private
14653     loadResponse : function(o, success, response){
14654         delete this.activeRequest;
14655         if(!success){
14656             this.fireEvent("loadexception", this, o, response);
14657             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14658             return;
14659         }
14660         var result;
14661         try {
14662             result = o.reader.read(response);
14663         }catch(e){
14664             this.fireEvent("loadexception", this, o, response, e);
14665             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14666             return;
14667         }
14668         
14669         this.fireEvent("load", this, o, o.request.arg);
14670         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14671     },
14672
14673     // private
14674     update : function(dataSet){
14675
14676     },
14677
14678     // private
14679     updateResponse : function(dataSet){
14680
14681     }
14682 });/*
14683  * Based on:
14684  * Ext JS Library 1.1.1
14685  * Copyright(c) 2006-2007, Ext JS, LLC.
14686  *
14687  * Originally Released Under LGPL - original licence link has changed is not relivant.
14688  *
14689  * Fork - LGPL
14690  * <script type="text/javascript">
14691  */
14692
14693 /**
14694  * @class Roo.data.ScriptTagProxy
14695  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14696  * other than the originating domain of the running page.<br><br>
14697  * <p>
14698  * <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
14699  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14700  * <p>
14701  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14702  * source code that is used as the source inside a &lt;script> tag.<br><br>
14703  * <p>
14704  * In order for the browser to process the returned data, the server must wrap the data object
14705  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14706  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14707  * depending on whether the callback name was passed:
14708  * <p>
14709  * <pre><code>
14710 boolean scriptTag = false;
14711 String cb = request.getParameter("callback");
14712 if (cb != null) {
14713     scriptTag = true;
14714     response.setContentType("text/javascript");
14715 } else {
14716     response.setContentType("application/x-json");
14717 }
14718 Writer out = response.getWriter();
14719 if (scriptTag) {
14720     out.write(cb + "(");
14721 }
14722 out.print(dataBlock.toJsonString());
14723 if (scriptTag) {
14724     out.write(");");
14725 }
14726 </pre></code>
14727  *
14728  * @constructor
14729  * @param {Object} config A configuration object.
14730  */
14731 Roo.data.ScriptTagProxy = function(config){
14732     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14733     Roo.apply(this, config);
14734     this.head = document.getElementsByTagName("head")[0];
14735 };
14736
14737 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14738
14739 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14740     /**
14741      * @cfg {String} url The URL from which to request the data object.
14742      */
14743     /**
14744      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14745      */
14746     timeout : 30000,
14747     /**
14748      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14749      * the server the name of the callback function set up by the load call to process the returned data object.
14750      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14751      * javascript output which calls this named function passing the data object as its only parameter.
14752      */
14753     callbackParam : "callback",
14754     /**
14755      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14756      * name to the request.
14757      */
14758     nocache : true,
14759
14760     /**
14761      * Load data from the configured URL, read the data object into
14762      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14763      * process that block using the passed callback.
14764      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14765      * for the request to the remote server.
14766      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14767      * object into a block of Roo.data.Records.
14768      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14769      * The function must be passed <ul>
14770      * <li>The Record block object</li>
14771      * <li>The "arg" argument from the load function</li>
14772      * <li>A boolean success indicator</li>
14773      * </ul>
14774      * @param {Object} scope The scope in which to call the callback
14775      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14776      */
14777     load : function(params, reader, callback, scope, arg){
14778         if(this.fireEvent("beforeload", this, params) !== false){
14779
14780             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14781
14782             var url = this.url;
14783             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14784             if(this.nocache){
14785                 url += "&_dc=" + (new Date().getTime());
14786             }
14787             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14788             var trans = {
14789                 id : transId,
14790                 cb : "stcCallback"+transId,
14791                 scriptId : "stcScript"+transId,
14792                 params : params,
14793                 arg : arg,
14794                 url : url,
14795                 callback : callback,
14796                 scope : scope,
14797                 reader : reader
14798             };
14799             var conn = this;
14800
14801             window[trans.cb] = function(o){
14802                 conn.handleResponse(o, trans);
14803             };
14804
14805             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14806
14807             if(this.autoAbort !== false){
14808                 this.abort();
14809             }
14810
14811             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14812
14813             var script = document.createElement("script");
14814             script.setAttribute("src", url);
14815             script.setAttribute("type", "text/javascript");
14816             script.setAttribute("id", trans.scriptId);
14817             this.head.appendChild(script);
14818
14819             this.trans = trans;
14820         }else{
14821             callback.call(scope||this, null, arg, false);
14822         }
14823     },
14824
14825     // private
14826     isLoading : function(){
14827         return this.trans ? true : false;
14828     },
14829
14830     /**
14831      * Abort the current server request.
14832      */
14833     abort : function(){
14834         if(this.isLoading()){
14835             this.destroyTrans(this.trans);
14836         }
14837     },
14838
14839     // private
14840     destroyTrans : function(trans, isLoaded){
14841         this.head.removeChild(document.getElementById(trans.scriptId));
14842         clearTimeout(trans.timeoutId);
14843         if(isLoaded){
14844             window[trans.cb] = undefined;
14845             try{
14846                 delete window[trans.cb];
14847             }catch(e){}
14848         }else{
14849             // if hasn't been loaded, wait for load to remove it to prevent script error
14850             window[trans.cb] = function(){
14851                 window[trans.cb] = undefined;
14852                 try{
14853                     delete window[trans.cb];
14854                 }catch(e){}
14855             };
14856         }
14857     },
14858
14859     // private
14860     handleResponse : function(o, trans){
14861         this.trans = false;
14862         this.destroyTrans(trans, true);
14863         var result;
14864         try {
14865             result = trans.reader.readRecords(o);
14866         }catch(e){
14867             this.fireEvent("loadexception", this, o, trans.arg, e);
14868             trans.callback.call(trans.scope||window, null, trans.arg, false);
14869             return;
14870         }
14871         this.fireEvent("load", this, o, trans.arg);
14872         trans.callback.call(trans.scope||window, result, trans.arg, true);
14873     },
14874
14875     // private
14876     handleFailure : function(trans){
14877         this.trans = false;
14878         this.destroyTrans(trans, false);
14879         this.fireEvent("loadexception", this, null, trans.arg);
14880         trans.callback.call(trans.scope||window, null, trans.arg, false);
14881     }
14882 });/*
14883  * Based on:
14884  * Ext JS Library 1.1.1
14885  * Copyright(c) 2006-2007, Ext JS, LLC.
14886  *
14887  * Originally Released Under LGPL - original licence link has changed is not relivant.
14888  *
14889  * Fork - LGPL
14890  * <script type="text/javascript">
14891  */
14892
14893 /**
14894  * @class Roo.data.JsonReader
14895  * @extends Roo.data.DataReader
14896  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14897  * based on mappings in a provided Roo.data.Record constructor.
14898  * 
14899  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14900  * in the reply previously. 
14901  * 
14902  * <p>
14903  * Example code:
14904  * <pre><code>
14905 var RecordDef = Roo.data.Record.create([
14906     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14907     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14908 ]);
14909 var myReader = new Roo.data.JsonReader({
14910     totalProperty: "results",    // The property which contains the total dataset size (optional)
14911     root: "rows",                // The property which contains an Array of row objects
14912     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14913 }, RecordDef);
14914 </code></pre>
14915  * <p>
14916  * This would consume a JSON file like this:
14917  * <pre><code>
14918 { 'results': 2, 'rows': [
14919     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14920     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14921 }
14922 </code></pre>
14923  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14924  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14925  * paged from the remote server.
14926  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14927  * @cfg {String} root name of the property which contains the Array of row objects.
14928  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14929  * @cfg {Array} fields Array of field definition objects
14930  * @constructor
14931  * Create a new JsonReader
14932  * @param {Object} meta Metadata configuration options
14933  * @param {Object} recordType Either an Array of field definition objects,
14934  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14935  */
14936 Roo.data.JsonReader = function(meta, recordType){
14937     
14938     meta = meta || {};
14939     // set some defaults:
14940     Roo.applyIf(meta, {
14941         totalProperty: 'total',
14942         successProperty : 'success',
14943         root : 'data',
14944         id : 'id'
14945     });
14946     
14947     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14948 };
14949 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14950     
14951     readerType : 'Json',
14952     
14953     /**
14954      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14955      * Used by Store query builder to append _requestMeta to params.
14956      * 
14957      */
14958     metaFromRemote : false,
14959     /**
14960      * This method is only used by a DataProxy which has retrieved data from a remote server.
14961      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14962      * @return {Object} data A data block which is used by an Roo.data.Store object as
14963      * a cache of Roo.data.Records.
14964      */
14965     read : function(response){
14966         var json = response.responseText;
14967        
14968         var o = /* eval:var:o */ eval("("+json+")");
14969         if(!o) {
14970             throw {message: "JsonReader.read: Json object not found"};
14971         }
14972         
14973         if(o.metaData){
14974             
14975             delete this.ef;
14976             this.metaFromRemote = true;
14977             this.meta = o.metaData;
14978             this.recordType = Roo.data.Record.create(o.metaData.fields);
14979             this.onMetaChange(this.meta, this.recordType, o);
14980         }
14981         return this.readRecords(o);
14982     },
14983
14984     // private function a store will implement
14985     onMetaChange : function(meta, recordType, o){
14986
14987     },
14988
14989     /**
14990          * @ignore
14991          */
14992     simpleAccess: function(obj, subsc) {
14993         return obj[subsc];
14994     },
14995
14996         /**
14997          * @ignore
14998          */
14999     getJsonAccessor: function(){
15000         var re = /[\[\.]/;
15001         return function(expr) {
15002             try {
15003                 return(re.test(expr))
15004                     ? new Function("obj", "return obj." + expr)
15005                     : function(obj){
15006                         return obj[expr];
15007                     };
15008             } catch(e){}
15009             return Roo.emptyFn;
15010         };
15011     }(),
15012
15013     /**
15014      * Create a data block containing Roo.data.Records from an XML document.
15015      * @param {Object} o An object which contains an Array of row objects in the property specified
15016      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15017      * which contains the total size of the dataset.
15018      * @return {Object} data A data block which is used by an Roo.data.Store object as
15019      * a cache of Roo.data.Records.
15020      */
15021     readRecords : function(o){
15022         /**
15023          * After any data loads, the raw JSON data is available for further custom processing.
15024          * @type Object
15025          */
15026         this.o = o;
15027         var s = this.meta, Record = this.recordType,
15028             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15029
15030 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15031         if (!this.ef) {
15032             if(s.totalProperty) {
15033                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15034                 }
15035                 if(s.successProperty) {
15036                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15037                 }
15038                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15039                 if (s.id) {
15040                         var g = this.getJsonAccessor(s.id);
15041                         this.getId = function(rec) {
15042                                 var r = g(rec);  
15043                                 return (r === undefined || r === "") ? null : r;
15044                         };
15045                 } else {
15046                         this.getId = function(){return null;};
15047                 }
15048             this.ef = [];
15049             for(var jj = 0; jj < fl; jj++){
15050                 f = fi[jj];
15051                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15052                 this.ef[jj] = this.getJsonAccessor(map);
15053             }
15054         }
15055
15056         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15057         if(s.totalProperty){
15058             var vt = parseInt(this.getTotal(o), 10);
15059             if(!isNaN(vt)){
15060                 totalRecords = vt;
15061             }
15062         }
15063         if(s.successProperty){
15064             var vs = this.getSuccess(o);
15065             if(vs === false || vs === 'false'){
15066                 success = false;
15067             }
15068         }
15069         var records = [];
15070         for(var i = 0; i < c; i++){
15071                 var n = root[i];
15072             var values = {};
15073             var id = this.getId(n);
15074             for(var j = 0; j < fl; j++){
15075                 f = fi[j];
15076             var v = this.ef[j](n);
15077             if (!f.convert) {
15078                 Roo.log('missing convert for ' + f.name);
15079                 Roo.log(f);
15080                 continue;
15081             }
15082             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15083             }
15084             var record = new Record(values, id);
15085             record.json = n;
15086             records[i] = record;
15087         }
15088         return {
15089             raw : o,
15090             success : success,
15091             records : records,
15092             totalRecords : totalRecords
15093         };
15094     },
15095     // used when loading children.. @see loadDataFromChildren
15096     toLoadData: function(rec)
15097     {
15098         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15099         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15100         return { data : data, total : data.length };
15101         
15102     }
15103 });/*
15104  * Based on:
15105  * Ext JS Library 1.1.1
15106  * Copyright(c) 2006-2007, Ext JS, LLC.
15107  *
15108  * Originally Released Under LGPL - original licence link has changed is not relivant.
15109  *
15110  * Fork - LGPL
15111  * <script type="text/javascript">
15112  */
15113
15114 /**
15115  * @class Roo.data.ArrayReader
15116  * @extends Roo.data.DataReader
15117  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15118  * Each element of that Array represents a row of data fields. The
15119  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15120  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15121  * <p>
15122  * Example code:.
15123  * <pre><code>
15124 var RecordDef = Roo.data.Record.create([
15125     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15126     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15127 ]);
15128 var myReader = new Roo.data.ArrayReader({
15129     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15130 }, RecordDef);
15131 </code></pre>
15132  * <p>
15133  * This would consume an Array like this:
15134  * <pre><code>
15135 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15136   </code></pre>
15137  
15138  * @constructor
15139  * Create a new JsonReader
15140  * @param {Object} meta Metadata configuration options.
15141  * @param {Object|Array} recordType Either an Array of field definition objects
15142  * 
15143  * @cfg {Array} fields Array of field definition objects
15144  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15145  * as specified to {@link Roo.data.Record#create},
15146  * or an {@link Roo.data.Record} object
15147  *
15148  * 
15149  * created using {@link Roo.data.Record#create}.
15150  */
15151 Roo.data.ArrayReader = function(meta, recordType)
15152 {    
15153     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15154 };
15155
15156 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15157     
15158       /**
15159      * Create a data block containing Roo.data.Records from an XML document.
15160      * @param {Object} o An Array of row objects which represents the dataset.
15161      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15162      * a cache of Roo.data.Records.
15163      */
15164     readRecords : function(o)
15165     {
15166         var sid = this.meta ? this.meta.id : null;
15167         var recordType = this.recordType, fields = recordType.prototype.fields;
15168         var records = [];
15169         var root = o;
15170         for(var i = 0; i < root.length; i++){
15171             var n = root[i];
15172             var values = {};
15173             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15174             for(var j = 0, jlen = fields.length; j < jlen; j++){
15175                 var f = fields.items[j];
15176                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15177                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15178                 v = f.convert(v);
15179                 values[f.name] = v;
15180             }
15181             var record = new recordType(values, id);
15182             record.json = n;
15183             records[records.length] = record;
15184         }
15185         return {
15186             records : records,
15187             totalRecords : records.length
15188         };
15189     },
15190     // used when loading children.. @see loadDataFromChildren
15191     toLoadData: function(rec)
15192     {
15193         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15194         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15195         
15196     }
15197     
15198     
15199 });/*
15200  * - LGPL
15201  * * 
15202  */
15203
15204 /**
15205  * @class Roo.bootstrap.ComboBox
15206  * @extends Roo.bootstrap.TriggerField
15207  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15208  * @cfg {Boolean} append (true|false) default false
15209  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15210  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15211  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15212  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15213  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15214  * @cfg {Boolean} animate default true
15215  * @cfg {Boolean} emptyResultText only for touch device
15216  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15217  * @cfg {String} emptyTitle default ''
15218  * @cfg {Number} width fixed with? experimental
15219  * @constructor
15220  * Create a new ComboBox.
15221  * @param {Object} config Configuration options
15222  */
15223 Roo.bootstrap.ComboBox = function(config){
15224     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15225     this.addEvents({
15226         /**
15227          * @event expand
15228          * Fires when the dropdown list is expanded
15229         * @param {Roo.bootstrap.ComboBox} combo This combo box
15230         */
15231         'expand' : true,
15232         /**
15233          * @event collapse
15234          * Fires when the dropdown list is collapsed
15235         * @param {Roo.bootstrap.ComboBox} combo This combo box
15236         */
15237         'collapse' : true,
15238         /**
15239          * @event beforeselect
15240          * Fires before a list item is selected. Return false to cancel the selection.
15241         * @param {Roo.bootstrap.ComboBox} combo This combo box
15242         * @param {Roo.data.Record} record The data record returned from the underlying store
15243         * @param {Number} index The index of the selected item in the dropdown list
15244         */
15245         'beforeselect' : true,
15246         /**
15247          * @event select
15248          * Fires when a list item is selected
15249         * @param {Roo.bootstrap.ComboBox} combo This combo box
15250         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15251         * @param {Number} index The index of the selected item in the dropdown list
15252         */
15253         'select' : true,
15254         /**
15255          * @event beforequery
15256          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15257          * The event object passed has these properties:
15258         * @param {Roo.bootstrap.ComboBox} combo This combo box
15259         * @param {String} query The query
15260         * @param {Boolean} forceAll true to force "all" query
15261         * @param {Boolean} cancel true to cancel the query
15262         * @param {Object} e The query event object
15263         */
15264         'beforequery': true,
15265          /**
15266          * @event add
15267          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15268         * @param {Roo.bootstrap.ComboBox} combo This combo box
15269         */
15270         'add' : true,
15271         /**
15272          * @event edit
15273          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15274         * @param {Roo.bootstrap.ComboBox} combo This combo box
15275         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15276         */
15277         'edit' : true,
15278         /**
15279          * @event remove
15280          * Fires when the remove value from the combobox array
15281         * @param {Roo.bootstrap.ComboBox} combo This combo box
15282         */
15283         'remove' : true,
15284         /**
15285          * @event afterremove
15286          * Fires when the remove value from the combobox array
15287         * @param {Roo.bootstrap.ComboBox} combo This combo box
15288         */
15289         'afterremove' : true,
15290         /**
15291          * @event specialfilter
15292          * Fires when specialfilter
15293             * @param {Roo.bootstrap.ComboBox} combo This combo box
15294             */
15295         'specialfilter' : true,
15296         /**
15297          * @event tick
15298          * Fires when tick the element
15299             * @param {Roo.bootstrap.ComboBox} combo This combo box
15300             */
15301         'tick' : true,
15302         /**
15303          * @event touchviewdisplay
15304          * Fires when touch view require special display (default is using displayField)
15305             * @param {Roo.bootstrap.ComboBox} combo This combo box
15306             * @param {Object} cfg set html .
15307             */
15308         'touchviewdisplay' : true
15309         
15310     });
15311     
15312     this.item = [];
15313     this.tickItems = [];
15314     
15315     this.selectedIndex = -1;
15316     if(this.mode == 'local'){
15317         if(config.queryDelay === undefined){
15318             this.queryDelay = 10;
15319         }
15320         if(config.minChars === undefined){
15321             this.minChars = 0;
15322         }
15323     }
15324 };
15325
15326 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15327      
15328     /**
15329      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15330      * rendering into an Roo.Editor, defaults to false)
15331      */
15332     /**
15333      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15334      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15335      */
15336     /**
15337      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15338      */
15339     /**
15340      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15341      * the dropdown list (defaults to undefined, with no header element)
15342      */
15343
15344      /**
15345      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15346      */
15347      
15348      /**
15349      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15350      */
15351     listWidth: undefined,
15352     /**
15353      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15354      * mode = 'remote' or 'text' if mode = 'local')
15355      */
15356     displayField: undefined,
15357     
15358     /**
15359      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15360      * mode = 'remote' or 'value' if mode = 'local'). 
15361      * Note: use of a valueField requires the user make a selection
15362      * in order for a value to be mapped.
15363      */
15364     valueField: undefined,
15365     /**
15366      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15367      */
15368     modalTitle : '',
15369     
15370     /**
15371      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15372      * field's data value (defaults to the underlying DOM element's name)
15373      */
15374     hiddenName: undefined,
15375     /**
15376      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15377      */
15378     listClass: '',
15379     /**
15380      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15381      */
15382     selectedClass: 'active',
15383     
15384     /**
15385      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15386      */
15387     shadow:'sides',
15388     /**
15389      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15390      * anchor positions (defaults to 'tl-bl')
15391      */
15392     listAlign: 'tl-bl?',
15393     /**
15394      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15395      */
15396     maxHeight: 300,
15397     /**
15398      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15399      * query specified by the allQuery config option (defaults to 'query')
15400      */
15401     triggerAction: 'query',
15402     /**
15403      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15404      * (defaults to 4, does not apply if editable = false)
15405      */
15406     minChars : 4,
15407     /**
15408      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15409      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15410      */
15411     typeAhead: false,
15412     /**
15413      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15414      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15415      */
15416     queryDelay: 500,
15417     /**
15418      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15419      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15420      */
15421     pageSize: 0,
15422     /**
15423      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15424      * when editable = true (defaults to false)
15425      */
15426     selectOnFocus:false,
15427     /**
15428      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15429      */
15430     queryParam: 'query',
15431     /**
15432      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15433      * when mode = 'remote' (defaults to 'Loading...')
15434      */
15435     loadingText: 'Loading...',
15436     /**
15437      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15438      */
15439     resizable: false,
15440     /**
15441      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15442      */
15443     handleHeight : 8,
15444     /**
15445      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15446      * traditional select (defaults to true)
15447      */
15448     editable: true,
15449     /**
15450      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15451      */
15452     allQuery: '',
15453     /**
15454      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15455      */
15456     mode: 'remote',
15457     /**
15458      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15459      * listWidth has a higher value)
15460      */
15461     minListWidth : 70,
15462     /**
15463      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15464      * allow the user to set arbitrary text into the field (defaults to false)
15465      */
15466     forceSelection:false,
15467     /**
15468      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15469      * if typeAhead = true (defaults to 250)
15470      */
15471     typeAheadDelay : 250,
15472     /**
15473      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15474      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15475      */
15476     valueNotFoundText : undefined,
15477     /**
15478      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15479      */
15480     blockFocus : false,
15481     
15482     /**
15483      * @cfg {Boolean} disableClear Disable showing of clear button.
15484      */
15485     disableClear : false,
15486     /**
15487      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15488      */
15489     alwaysQuery : false,
15490     
15491     /**
15492      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15493      */
15494     multiple : false,
15495     
15496     /**
15497      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15498      */
15499     invalidClass : "has-warning",
15500     
15501     /**
15502      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15503      */
15504     validClass : "has-success",
15505     
15506     /**
15507      * @cfg {Boolean} specialFilter (true|false) special filter default false
15508      */
15509     specialFilter : false,
15510     
15511     /**
15512      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15513      */
15514     mobileTouchView : true,
15515     
15516     /**
15517      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15518      */
15519     useNativeIOS : false,
15520     
15521     /**
15522      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15523      */
15524     mobile_restrict_height : false,
15525     
15526     ios_options : false,
15527     
15528     //private
15529     addicon : false,
15530     editicon: false,
15531     
15532     page: 0,
15533     hasQuery: false,
15534     append: false,
15535     loadNext: false,
15536     autoFocus : true,
15537     tickable : false,
15538     btnPosition : 'right',
15539     triggerList : true,
15540     showToggleBtn : true,
15541     animate : true,
15542     emptyResultText: 'Empty',
15543     triggerText : 'Select',
15544     emptyTitle : '',
15545     width : false,
15546     
15547     // element that contains real text value.. (when hidden is used..)
15548     
15549     getAutoCreate : function()
15550     {   
15551         var cfg = false;
15552         //render
15553         /*
15554          * Render classic select for iso
15555          */
15556         
15557         if(Roo.isIOS && this.useNativeIOS){
15558             cfg = this.getAutoCreateNativeIOS();
15559             return cfg;
15560         }
15561         
15562         /*
15563          * Touch Devices
15564          */
15565         
15566         if(Roo.isTouch && this.mobileTouchView){
15567             cfg = this.getAutoCreateTouchView();
15568             return cfg;;
15569         }
15570         
15571         /*
15572          *  Normal ComboBox
15573          */
15574         if(!this.tickable){
15575             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15576             return cfg;
15577         }
15578         
15579         /*
15580          *  ComboBox with tickable selections
15581          */
15582              
15583         var align = this.labelAlign || this.parentLabelAlign();
15584         
15585         cfg = {
15586             cls : 'form-group roo-combobox-tickable' //input-group
15587         };
15588         
15589         var btn_text_select = '';
15590         var btn_text_done = '';
15591         var btn_text_cancel = '';
15592         
15593         if (this.btn_text_show) {
15594             btn_text_select = 'Select';
15595             btn_text_done = 'Done';
15596             btn_text_cancel = 'Cancel'; 
15597         }
15598         
15599         var buttons = {
15600             tag : 'div',
15601             cls : 'tickable-buttons',
15602             cn : [
15603                 {
15604                     tag : 'button',
15605                     type : 'button',
15606                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15607                     //html : this.triggerText
15608                     html: btn_text_select
15609                 },
15610                 {
15611                     tag : 'button',
15612                     type : 'button',
15613                     name : 'ok',
15614                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15615                     //html : 'Done'
15616                     html: btn_text_done
15617                 },
15618                 {
15619                     tag : 'button',
15620                     type : 'button',
15621                     name : 'cancel',
15622                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15623                     //html : 'Cancel'
15624                     html: btn_text_cancel
15625                 }
15626             ]
15627         };
15628         
15629         if(this.editable){
15630             buttons.cn.unshift({
15631                 tag: 'input',
15632                 cls: 'roo-select2-search-field-input'
15633             });
15634         }
15635         
15636         var _this = this;
15637         
15638         Roo.each(buttons.cn, function(c){
15639             if (_this.size) {
15640                 c.cls += ' btn-' + _this.size;
15641             }
15642
15643             if (_this.disabled) {
15644                 c.disabled = true;
15645             }
15646         });
15647         
15648         var box = {
15649             tag: 'div',
15650             style : 'display: contents',
15651             cn: [
15652                 {
15653                     tag: 'input',
15654                     type : 'hidden',
15655                     cls: 'form-hidden-field'
15656                 },
15657                 {
15658                     tag: 'ul',
15659                     cls: 'roo-select2-choices',
15660                     cn:[
15661                         {
15662                             tag: 'li',
15663                             cls: 'roo-select2-search-field',
15664                             cn: [
15665                                 buttons
15666                             ]
15667                         }
15668                     ]
15669                 }
15670             ]
15671         };
15672         
15673         var combobox = {
15674             cls: 'roo-select2-container input-group roo-select2-container-multi',
15675             cn: [
15676                 
15677                 box
15678 //                {
15679 //                    tag: 'ul',
15680 //                    cls: 'typeahead typeahead-long dropdown-menu',
15681 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15682 //                }
15683             ]
15684         };
15685         
15686         if(this.hasFeedback && !this.allowBlank){
15687             
15688             var feedback = {
15689                 tag: 'span',
15690                 cls: 'glyphicon form-control-feedback'
15691             };
15692
15693             combobox.cn.push(feedback);
15694         }
15695         
15696         
15697         
15698         var indicator = {
15699             tag : 'i',
15700             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15701             tooltip : 'This field is required'
15702         };
15703         if (Roo.bootstrap.version == 4) {
15704             indicator = {
15705                 tag : 'i',
15706                 style : 'display:none'
15707             };
15708         }
15709         if (align ==='left' && this.fieldLabel.length) {
15710             
15711             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15712             
15713             cfg.cn = [
15714                 indicator,
15715                 {
15716                     tag: 'label',
15717                     'for' :  id,
15718                     cls : 'control-label col-form-label',
15719                     html : this.fieldLabel
15720
15721                 },
15722                 {
15723                     cls : "", 
15724                     cn: [
15725                         combobox
15726                     ]
15727                 }
15728
15729             ];
15730             
15731             var labelCfg = cfg.cn[1];
15732             var contentCfg = cfg.cn[2];
15733             
15734
15735             if(this.indicatorpos == 'right'){
15736                 
15737                 cfg.cn = [
15738                     {
15739                         tag: 'label',
15740                         'for' :  id,
15741                         cls : 'control-label col-form-label',
15742                         cn : [
15743                             {
15744                                 tag : 'span',
15745                                 html : this.fieldLabel
15746                             },
15747                             indicator
15748                         ]
15749                     },
15750                     {
15751                         cls : "",
15752                         cn: [
15753                             combobox
15754                         ]
15755                     }
15756
15757                 ];
15758                 
15759                 
15760                 
15761                 labelCfg = cfg.cn[0];
15762                 contentCfg = cfg.cn[1];
15763             
15764             }
15765             
15766             if(this.labelWidth > 12){
15767                 labelCfg.style = "width: " + this.labelWidth + 'px';
15768             }
15769             if(this.width * 1 > 0){
15770                 contentCfg.style = "width: " + this.width + 'px';
15771             }
15772             if(this.labelWidth < 13 && this.labelmd == 0){
15773                 this.labelmd = this.labelWidth;
15774             }
15775             
15776             if(this.labellg > 0){
15777                 labelCfg.cls += ' col-lg-' + this.labellg;
15778                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15779             }
15780             
15781             if(this.labelmd > 0){
15782                 labelCfg.cls += ' col-md-' + this.labelmd;
15783                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15784             }
15785             
15786             if(this.labelsm > 0){
15787                 labelCfg.cls += ' col-sm-' + this.labelsm;
15788                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15789             }
15790             
15791             if(this.labelxs > 0){
15792                 labelCfg.cls += ' col-xs-' + this.labelxs;
15793                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15794             }
15795                 
15796                 
15797         } else if ( this.fieldLabel.length) {
15798 //                Roo.log(" label");
15799                  cfg.cn = [
15800                    indicator,
15801                     {
15802                         tag: 'label',
15803                         //cls : 'input-group-addon',
15804                         html : this.fieldLabel
15805                     },
15806                     combobox
15807                 ];
15808                 
15809                 if(this.indicatorpos == 'right'){
15810                     cfg.cn = [
15811                         {
15812                             tag: 'label',
15813                             //cls : 'input-group-addon',
15814                             html : this.fieldLabel
15815                         },
15816                         indicator,
15817                         combobox
15818                     ];
15819                     
15820                 }
15821
15822         } else {
15823             
15824 //                Roo.log(" no label && no align");
15825                 cfg = combobox
15826                      
15827                 
15828         }
15829          
15830         var settings=this;
15831         ['xs','sm','md','lg'].map(function(size){
15832             if (settings[size]) {
15833                 cfg.cls += ' col-' + size + '-' + settings[size];
15834             }
15835         });
15836         
15837         return cfg;
15838         
15839     },
15840     
15841     _initEventsCalled : false,
15842     
15843     // private
15844     initEvents: function()
15845     {   
15846         if (this._initEventsCalled) { // as we call render... prevent looping...
15847             return;
15848         }
15849         this._initEventsCalled = true;
15850         
15851         if (!this.store) {
15852             throw "can not find store for combo";
15853         }
15854         
15855         this.indicator = this.indicatorEl();
15856         
15857         this.store = Roo.factory(this.store, Roo.data);
15858         this.store.parent = this;
15859         
15860         // if we are building from html. then this element is so complex, that we can not really
15861         // use the rendered HTML.
15862         // so we have to trash and replace the previous code.
15863         if (Roo.XComponent.build_from_html) {
15864             // remove this element....
15865             var e = this.el.dom, k=0;
15866             while (e ) { e = e.previousSibling;  ++k;}
15867
15868             this.el.remove();
15869             
15870             this.el=false;
15871             this.rendered = false;
15872             
15873             this.render(this.parent().getChildContainer(true), k);
15874         }
15875         
15876         if(Roo.isIOS && this.useNativeIOS){
15877             this.initIOSView();
15878             return;
15879         }
15880         
15881         /*
15882          * Touch Devices
15883          */
15884         
15885         if(Roo.isTouch && this.mobileTouchView){
15886             this.initTouchView();
15887             return;
15888         }
15889         
15890         if(this.tickable){
15891             this.initTickableEvents();
15892             return;
15893         }
15894         
15895         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15896         
15897         if(this.hiddenName){
15898             
15899             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15900             
15901             this.hiddenField.dom.value =
15902                 this.hiddenValue !== undefined ? this.hiddenValue :
15903                 this.value !== undefined ? this.value : '';
15904
15905             // prevent input submission
15906             this.el.dom.removeAttribute('name');
15907             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15908              
15909              
15910         }
15911         //if(Roo.isGecko){
15912         //    this.el.dom.setAttribute('autocomplete', 'off');
15913         //}
15914         
15915         var cls = 'x-combo-list';
15916         
15917         //this.list = new Roo.Layer({
15918         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15919         //});
15920         
15921         var _this = this;
15922         
15923         (function(){
15924             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15925             _this.list.setWidth(lw);
15926         }).defer(100);
15927         
15928         this.list.on('mouseover', this.onViewOver, this);
15929         this.list.on('mousemove', this.onViewMove, this);
15930         this.list.on('scroll', this.onViewScroll, this);
15931         
15932         /*
15933         this.list.swallowEvent('mousewheel');
15934         this.assetHeight = 0;
15935
15936         if(this.title){
15937             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15938             this.assetHeight += this.header.getHeight();
15939         }
15940
15941         this.innerList = this.list.createChild({cls:cls+'-inner'});
15942         this.innerList.on('mouseover', this.onViewOver, this);
15943         this.innerList.on('mousemove', this.onViewMove, this);
15944         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15945         
15946         if(this.allowBlank && !this.pageSize && !this.disableClear){
15947             this.footer = this.list.createChild({cls:cls+'-ft'});
15948             this.pageTb = new Roo.Toolbar(this.footer);
15949            
15950         }
15951         if(this.pageSize){
15952             this.footer = this.list.createChild({cls:cls+'-ft'});
15953             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15954                     {pageSize: this.pageSize});
15955             
15956         }
15957         
15958         if (this.pageTb && this.allowBlank && !this.disableClear) {
15959             var _this = this;
15960             this.pageTb.add(new Roo.Toolbar.Fill(), {
15961                 cls: 'x-btn-icon x-btn-clear',
15962                 text: '&#160;',
15963                 handler: function()
15964                 {
15965                     _this.collapse();
15966                     _this.clearValue();
15967                     _this.onSelect(false, -1);
15968                 }
15969             });
15970         }
15971         if (this.footer) {
15972             this.assetHeight += this.footer.getHeight();
15973         }
15974         */
15975             
15976         if(!this.tpl){
15977             this.tpl = Roo.bootstrap.version == 4 ?
15978                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15979                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15980         }
15981
15982         this.view = new Roo.View(this.list, this.tpl, {
15983             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15984         });
15985         //this.view.wrapEl.setDisplayed(false);
15986         this.view.on('click', this.onViewClick, this);
15987         
15988         
15989         this.store.on('beforeload', this.onBeforeLoad, this);
15990         this.store.on('load', this.onLoad, this);
15991         this.store.on('loadexception', this.onLoadException, this);
15992         /*
15993         if(this.resizable){
15994             this.resizer = new Roo.Resizable(this.list,  {
15995                pinned:true, handles:'se'
15996             });
15997             this.resizer.on('resize', function(r, w, h){
15998                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15999                 this.listWidth = w;
16000                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16001                 this.restrictHeight();
16002             }, this);
16003             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16004         }
16005         */
16006         if(!this.editable){
16007             this.editable = true;
16008             this.setEditable(false);
16009         }
16010         
16011         /*
16012         
16013         if (typeof(this.events.add.listeners) != 'undefined') {
16014             
16015             this.addicon = this.wrap.createChild(
16016                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16017        
16018             this.addicon.on('click', function(e) {
16019                 this.fireEvent('add', this);
16020             }, this);
16021         }
16022         if (typeof(this.events.edit.listeners) != 'undefined') {
16023             
16024             this.editicon = this.wrap.createChild(
16025                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16026             if (this.addicon) {
16027                 this.editicon.setStyle('margin-left', '40px');
16028             }
16029             this.editicon.on('click', function(e) {
16030                 
16031                 // we fire even  if inothing is selected..
16032                 this.fireEvent('edit', this, this.lastData );
16033                 
16034             }, this);
16035         }
16036         */
16037         
16038         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16039             "up" : function(e){
16040                 this.inKeyMode = true;
16041                 this.selectPrev();
16042             },
16043
16044             "down" : function(e){
16045                 if(!this.isExpanded()){
16046                     this.onTriggerClick();
16047                 }else{
16048                     this.inKeyMode = true;
16049                     this.selectNext();
16050                 }
16051             },
16052
16053             "enter" : function(e){
16054 //                this.onViewClick();
16055                 //return true;
16056                 this.collapse();
16057                 
16058                 if(this.fireEvent("specialkey", this, e)){
16059                     this.onViewClick(false);
16060                 }
16061                 
16062                 return true;
16063             },
16064
16065             "esc" : function(e){
16066                 this.collapse();
16067             },
16068
16069             "tab" : function(e){
16070                 this.collapse();
16071                 
16072                 if(this.fireEvent("specialkey", this, e)){
16073                     this.onViewClick(false);
16074                 }
16075                 
16076                 return true;
16077             },
16078
16079             scope : this,
16080
16081             doRelay : function(foo, bar, hname){
16082                 if(hname == 'down' || this.scope.isExpanded()){
16083                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16084                 }
16085                 return true;
16086             },
16087
16088             forceKeyDown: true
16089         });
16090         
16091         
16092         this.queryDelay = Math.max(this.queryDelay || 10,
16093                 this.mode == 'local' ? 10 : 250);
16094         
16095         
16096         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16097         
16098         if(this.typeAhead){
16099             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16100         }
16101         if(this.editable !== false){
16102             this.inputEl().on("keyup", this.onKeyUp, this);
16103         }
16104         if(this.forceSelection){
16105             this.inputEl().on('blur', this.doForce, this);
16106         }
16107         
16108         if(this.multiple){
16109             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16110             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16111         }
16112     },
16113     
16114     initTickableEvents: function()
16115     {   
16116         this.createList();
16117         
16118         if(this.hiddenName){
16119             
16120             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16121             
16122             this.hiddenField.dom.value =
16123                 this.hiddenValue !== undefined ? this.hiddenValue :
16124                 this.value !== undefined ? this.value : '';
16125
16126             // prevent input submission
16127             this.el.dom.removeAttribute('name');
16128             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16129              
16130              
16131         }
16132         
16133 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16134         
16135         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16136         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16137         if(this.triggerList){
16138             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16139         }
16140          
16141         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16142         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16143         
16144         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16145         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16146         
16147         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16148         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16149         
16150         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16151         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16152         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16153         
16154         this.okBtn.hide();
16155         this.cancelBtn.hide();
16156         
16157         var _this = this;
16158         
16159         (function(){
16160             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16161             _this.list.setWidth(lw);
16162         }).defer(100);
16163         
16164         this.list.on('mouseover', this.onViewOver, this);
16165         this.list.on('mousemove', this.onViewMove, this);
16166         
16167         this.list.on('scroll', this.onViewScroll, this);
16168         
16169         if(!this.tpl){
16170             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16171                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16172         }
16173
16174         this.view = new Roo.View(this.list, this.tpl, {
16175             singleSelect:true,
16176             tickable:true,
16177             parent:this,
16178             store: this.store,
16179             selectedClass: this.selectedClass
16180         });
16181         
16182         //this.view.wrapEl.setDisplayed(false);
16183         this.view.on('click', this.onViewClick, this);
16184         
16185         
16186         
16187         this.store.on('beforeload', this.onBeforeLoad, this);
16188         this.store.on('load', this.onLoad, this);
16189         this.store.on('loadexception', this.onLoadException, this);
16190         
16191         if(this.editable){
16192             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16193                 "up" : function(e){
16194                     this.inKeyMode = true;
16195                     this.selectPrev();
16196                 },
16197
16198                 "down" : function(e){
16199                     this.inKeyMode = true;
16200                     this.selectNext();
16201                 },
16202
16203                 "enter" : function(e){
16204                     if(this.fireEvent("specialkey", this, e)){
16205                         this.onViewClick(false);
16206                     }
16207                     
16208                     return true;
16209                 },
16210
16211                 "esc" : function(e){
16212                     this.onTickableFooterButtonClick(e, false, false);
16213                 },
16214
16215                 "tab" : function(e){
16216                     this.fireEvent("specialkey", this, e);
16217                     
16218                     this.onTickableFooterButtonClick(e, false, false);
16219                     
16220                     return true;
16221                 },
16222
16223                 scope : this,
16224
16225                 doRelay : function(e, fn, key){
16226                     if(this.scope.isExpanded()){
16227                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16228                     }
16229                     return true;
16230                 },
16231
16232                 forceKeyDown: true
16233             });
16234         }
16235         
16236         this.queryDelay = Math.max(this.queryDelay || 10,
16237                 this.mode == 'local' ? 10 : 250);
16238         
16239         
16240         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16241         
16242         if(this.typeAhead){
16243             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16244         }
16245         
16246         if(this.editable !== false){
16247             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16248         }
16249         
16250         this.indicator = this.indicatorEl();
16251         
16252         if(this.indicator){
16253             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16254             this.indicator.hide();
16255         }
16256         
16257     },
16258
16259     onDestroy : function(){
16260         if(this.view){
16261             this.view.setStore(null);
16262             this.view.el.removeAllListeners();
16263             this.view.el.remove();
16264             this.view.purgeListeners();
16265         }
16266         if(this.list){
16267             this.list.dom.innerHTML  = '';
16268         }
16269         
16270         if(this.store){
16271             this.store.un('beforeload', this.onBeforeLoad, this);
16272             this.store.un('load', this.onLoad, this);
16273             this.store.un('loadexception', this.onLoadException, this);
16274         }
16275         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16276     },
16277
16278     // private
16279     fireKey : function(e){
16280         if(e.isNavKeyPress() && !this.list.isVisible()){
16281             this.fireEvent("specialkey", this, e);
16282         }
16283     },
16284
16285     // private
16286     onResize: function(w, h)
16287     {
16288         
16289         
16290 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16291 //        
16292 //        if(typeof w != 'number'){
16293 //            // we do not handle it!?!?
16294 //            return;
16295 //        }
16296 //        var tw = this.trigger.getWidth();
16297 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16298 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16299 //        var x = w - tw;
16300 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16301 //            
16302 //        //this.trigger.setStyle('left', x+'px');
16303 //        
16304 //        if(this.list && this.listWidth === undefined){
16305 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16306 //            this.list.setWidth(lw);
16307 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16308 //        }
16309         
16310     
16311         
16312     },
16313
16314     /**
16315      * Allow or prevent the user from directly editing the field text.  If false is passed,
16316      * the user will only be able to select from the items defined in the dropdown list.  This method
16317      * is the runtime equivalent of setting the 'editable' config option at config time.
16318      * @param {Boolean} value True to allow the user to directly edit the field text
16319      */
16320     setEditable : function(value){
16321         if(value == this.editable){
16322             return;
16323         }
16324         this.editable = value;
16325         if(!value){
16326             this.inputEl().dom.setAttribute('readOnly', true);
16327             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16328             this.inputEl().addClass('x-combo-noedit');
16329         }else{
16330             this.inputEl().dom.setAttribute('readOnly', false);
16331             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16332             this.inputEl().removeClass('x-combo-noedit');
16333         }
16334     },
16335
16336     // private
16337     
16338     onBeforeLoad : function(combo,opts){
16339         if(!this.hasFocus){
16340             return;
16341         }
16342          if (!opts.add) {
16343             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16344          }
16345         this.restrictHeight();
16346         this.selectedIndex = -1;
16347     },
16348
16349     // private
16350     onLoad : function(){
16351         
16352         this.hasQuery = false;
16353         
16354         if(!this.hasFocus){
16355             return;
16356         }
16357         
16358         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16359             this.loading.hide();
16360         }
16361         
16362         if(this.store.getCount() > 0){
16363             
16364             this.expand();
16365             this.restrictHeight();
16366             if(this.lastQuery == this.allQuery){
16367                 if(this.editable && !this.tickable){
16368                     this.inputEl().dom.select();
16369                 }
16370                 
16371                 if(
16372                     !this.selectByValue(this.value, true) &&
16373                     this.autoFocus && 
16374                     (
16375                         !this.store.lastOptions ||
16376                         typeof(this.store.lastOptions.add) == 'undefined' || 
16377                         this.store.lastOptions.add != true
16378                     )
16379                 ){
16380                     this.select(0, true);
16381                 }
16382             }else{
16383                 if(this.autoFocus){
16384                     this.selectNext();
16385                 }
16386                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16387                     this.taTask.delay(this.typeAheadDelay);
16388                 }
16389             }
16390         }else{
16391             this.onEmptyResults();
16392         }
16393         
16394         //this.el.focus();
16395     },
16396     // private
16397     onLoadException : function()
16398     {
16399         this.hasQuery = false;
16400         
16401         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16402             this.loading.hide();
16403         }
16404         
16405         if(this.tickable && this.editable){
16406             return;
16407         }
16408         
16409         this.collapse();
16410         // only causes errors at present
16411         //Roo.log(this.store.reader.jsonData);
16412         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16413             // fixme
16414             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16415         //}
16416         
16417         
16418     },
16419     // private
16420     onTypeAhead : function(){
16421         if(this.store.getCount() > 0){
16422             var r = this.store.getAt(0);
16423             var newValue = r.data[this.displayField];
16424             var len = newValue.length;
16425             var selStart = this.getRawValue().length;
16426             
16427             if(selStart != len){
16428                 this.setRawValue(newValue);
16429                 this.selectText(selStart, newValue.length);
16430             }
16431         }
16432     },
16433
16434     // private
16435     onSelect : function(record, index){
16436         
16437         if(this.fireEvent('beforeselect', this, record, index) !== false){
16438         
16439             this.setFromData(index > -1 ? record.data : false);
16440             
16441             this.collapse();
16442             this.fireEvent('select', this, record, index);
16443         }
16444     },
16445
16446     /**
16447      * Returns the currently selected field value or empty string if no value is set.
16448      * @return {String} value The selected value
16449      */
16450     getValue : function()
16451     {
16452         if(Roo.isIOS && this.useNativeIOS){
16453             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16454         }
16455         
16456         if(this.multiple){
16457             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16458         }
16459         
16460         if(this.valueField){
16461             return typeof this.value != 'undefined' ? this.value : '';
16462         }else{
16463             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16464         }
16465     },
16466     
16467     getRawValue : function()
16468     {
16469         if(Roo.isIOS && this.useNativeIOS){
16470             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16471         }
16472         
16473         var v = this.inputEl().getValue();
16474         
16475         return v;
16476     },
16477
16478     /**
16479      * Clears any text/value currently set in the field
16480      */
16481     clearValue : function(){
16482         
16483         if(this.hiddenField){
16484             this.hiddenField.dom.value = '';
16485         }
16486         this.value = '';
16487         this.setRawValue('');
16488         this.lastSelectionText = '';
16489         this.lastData = false;
16490         
16491         var close = this.closeTriggerEl();
16492         
16493         if(close){
16494             close.hide();
16495         }
16496         
16497         this.validate();
16498         
16499     },
16500
16501     /**
16502      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16503      * will be displayed in the field.  If the value does not match the data value of an existing item,
16504      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16505      * Otherwise the field will be blank (although the value will still be set).
16506      * @param {String} value The value to match
16507      */
16508     setValue : function(v)
16509     {
16510         if(Roo.isIOS && this.useNativeIOS){
16511             this.setIOSValue(v);
16512             return;
16513         }
16514         
16515         if(this.multiple){
16516             this.syncValue();
16517             return;
16518         }
16519         
16520         var text = v;
16521         if(this.valueField){
16522             var r = this.findRecord(this.valueField, v);
16523             if(r){
16524                 text = r.data[this.displayField];
16525             }else if(this.valueNotFoundText !== undefined){
16526                 text = this.valueNotFoundText;
16527             }
16528         }
16529         this.lastSelectionText = text;
16530         if(this.hiddenField){
16531             this.hiddenField.dom.value = v;
16532         }
16533         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16534         this.value = v;
16535         
16536         var close = this.closeTriggerEl();
16537         
16538         if(close){
16539             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16540         }
16541         
16542         this.validate();
16543     },
16544     /**
16545      * @property {Object} the last set data for the element
16546      */
16547     
16548     lastData : false,
16549     /**
16550      * Sets the value of the field based on a object which is related to the record format for the store.
16551      * @param {Object} value the value to set as. or false on reset?
16552      */
16553     setFromData : function(o){
16554         
16555         if(this.multiple){
16556             this.addItem(o);
16557             return;
16558         }
16559             
16560         var dv = ''; // display value
16561         var vv = ''; // value value..
16562         this.lastData = o;
16563         if (this.displayField) {
16564             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16565         } else {
16566             // this is an error condition!!!
16567             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16568         }
16569         
16570         if(this.valueField){
16571             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16572         }
16573         
16574         var close = this.closeTriggerEl();
16575         
16576         if(close){
16577             if(dv.length || vv * 1 > 0){
16578                 close.show() ;
16579                 this.blockFocus=true;
16580             } else {
16581                 close.hide();
16582             }             
16583         }
16584         
16585         if(this.hiddenField){
16586             this.hiddenField.dom.value = vv;
16587             
16588             this.lastSelectionText = dv;
16589             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16590             this.value = vv;
16591             return;
16592         }
16593         // no hidden field.. - we store the value in 'value', but still display
16594         // display field!!!!
16595         this.lastSelectionText = dv;
16596         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16597         this.value = vv;
16598         
16599         
16600         
16601     },
16602     // private
16603     reset : function(){
16604         // overridden so that last data is reset..
16605         
16606         if(this.multiple){
16607             this.clearItem();
16608             return;
16609         }
16610         
16611         this.setValue(this.originalValue);
16612         //this.clearInvalid();
16613         this.lastData = false;
16614         if (this.view) {
16615             this.view.clearSelections();
16616         }
16617         
16618         this.validate();
16619     },
16620     // private
16621     findRecord : function(prop, value){
16622         var record;
16623         if(this.store.getCount() > 0){
16624             this.store.each(function(r){
16625                 if(r.data[prop] == value){
16626                     record = r;
16627                     return false;
16628                 }
16629                 return true;
16630             });
16631         }
16632         return record;
16633     },
16634     
16635     getName: function()
16636     {
16637         // returns hidden if it's set..
16638         if (!this.rendered) {return ''};
16639         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16640         
16641     },
16642     // private
16643     onViewMove : function(e, t){
16644         this.inKeyMode = false;
16645     },
16646
16647     // private
16648     onViewOver : function(e, t){
16649         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16650             return;
16651         }
16652         var item = this.view.findItemFromChild(t);
16653         
16654         if(item){
16655             var index = this.view.indexOf(item);
16656             this.select(index, false);
16657         }
16658     },
16659
16660     // private
16661     onViewClick : function(view, doFocus, el, e)
16662     {
16663         var index = this.view.getSelectedIndexes()[0];
16664         
16665         var r = this.store.getAt(index);
16666         
16667         if(this.tickable){
16668             
16669             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16670                 return;
16671             }
16672             
16673             var rm = false;
16674             var _this = this;
16675             
16676             Roo.each(this.tickItems, function(v,k){
16677                 
16678                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16679                     Roo.log(v);
16680                     _this.tickItems.splice(k, 1);
16681                     
16682                     if(typeof(e) == 'undefined' && view == false){
16683                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16684                     }
16685                     
16686                     rm = true;
16687                     return;
16688                 }
16689             });
16690             
16691             if(rm){
16692                 return;
16693             }
16694             
16695             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16696                 this.tickItems.push(r.data);
16697             }
16698             
16699             if(typeof(e) == 'undefined' && view == false){
16700                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16701             }
16702                     
16703             return;
16704         }
16705         
16706         if(r){
16707             this.onSelect(r, index);
16708         }
16709         if(doFocus !== false && !this.blockFocus){
16710             this.inputEl().focus();
16711         }
16712     },
16713
16714     // private
16715     restrictHeight : function(){
16716         //this.innerList.dom.style.height = '';
16717         //var inner = this.innerList.dom;
16718         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16719         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16720         //this.list.beginUpdate();
16721         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16722         this.list.alignTo(this.inputEl(), this.listAlign);
16723         this.list.alignTo(this.inputEl(), this.listAlign);
16724         //this.list.endUpdate();
16725     },
16726
16727     // private
16728     onEmptyResults : function(){
16729         
16730         if(this.tickable && this.editable){
16731             this.hasFocus = false;
16732             this.restrictHeight();
16733             return;
16734         }
16735         
16736         this.collapse();
16737     },
16738
16739     /**
16740      * Returns true if the dropdown list is expanded, else false.
16741      */
16742     isExpanded : function(){
16743         return this.list.isVisible();
16744     },
16745
16746     /**
16747      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16748      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16749      * @param {String} value The data value of the item to select
16750      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16751      * selected item if it is not currently in view (defaults to true)
16752      * @return {Boolean} True if the value matched an item in the list, else false
16753      */
16754     selectByValue : function(v, scrollIntoView){
16755         if(v !== undefined && v !== null){
16756             var r = this.findRecord(this.valueField || this.displayField, v);
16757             if(r){
16758                 this.select(this.store.indexOf(r), scrollIntoView);
16759                 return true;
16760             }
16761         }
16762         return false;
16763     },
16764
16765     /**
16766      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16767      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16768      * @param {Number} index The zero-based index of the list item to select
16769      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16770      * selected item if it is not currently in view (defaults to true)
16771      */
16772     select : function(index, scrollIntoView){
16773         this.selectedIndex = index;
16774         this.view.select(index);
16775         if(scrollIntoView !== false){
16776             var el = this.view.getNode(index);
16777             /*
16778              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16779              */
16780             if(el){
16781                 this.list.scrollChildIntoView(el, false);
16782             }
16783         }
16784     },
16785
16786     // private
16787     selectNext : function(){
16788         var ct = this.store.getCount();
16789         if(ct > 0){
16790             if(this.selectedIndex == -1){
16791                 this.select(0);
16792             }else if(this.selectedIndex < ct-1){
16793                 this.select(this.selectedIndex+1);
16794             }
16795         }
16796     },
16797
16798     // private
16799     selectPrev : function(){
16800         var ct = this.store.getCount();
16801         if(ct > 0){
16802             if(this.selectedIndex == -1){
16803                 this.select(0);
16804             }else if(this.selectedIndex != 0){
16805                 this.select(this.selectedIndex-1);
16806             }
16807         }
16808     },
16809
16810     // private
16811     onKeyUp : function(e){
16812         if(this.editable !== false && !e.isSpecialKey()){
16813             this.lastKey = e.getKey();
16814             this.dqTask.delay(this.queryDelay);
16815         }
16816     },
16817
16818     // private
16819     validateBlur : function(){
16820         return !this.list || !this.list.isVisible();   
16821     },
16822
16823     // private
16824     initQuery : function(){
16825         
16826         var v = this.getRawValue();
16827         
16828         if(this.tickable && this.editable){
16829             v = this.tickableInputEl().getValue();
16830         }
16831         
16832         this.doQuery(v);
16833     },
16834
16835     // private
16836     doForce : function(){
16837         if(this.inputEl().dom.value.length > 0){
16838             this.inputEl().dom.value =
16839                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16840              
16841         }
16842     },
16843
16844     /**
16845      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16846      * query allowing the query action to be canceled if needed.
16847      * @param {String} query The SQL query to execute
16848      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16849      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16850      * saved in the current store (defaults to false)
16851      */
16852     doQuery : function(q, forceAll){
16853         
16854         if(q === undefined || q === null){
16855             q = '';
16856         }
16857         var qe = {
16858             query: q,
16859             forceAll: forceAll,
16860             combo: this,
16861             cancel:false
16862         };
16863         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16864             return false;
16865         }
16866         q = qe.query;
16867         
16868         forceAll = qe.forceAll;
16869         if(forceAll === true || (q.length >= this.minChars)){
16870             
16871             this.hasQuery = true;
16872             
16873             if(this.lastQuery != q || this.alwaysQuery){
16874                 this.lastQuery = q;
16875                 if(this.mode == 'local'){
16876                     this.selectedIndex = -1;
16877                     if(forceAll){
16878                         this.store.clearFilter();
16879                     }else{
16880                         
16881                         if(this.specialFilter){
16882                             this.fireEvent('specialfilter', this);
16883                             this.onLoad();
16884                             return;
16885                         }
16886                         
16887                         this.store.filter(this.displayField, q);
16888                     }
16889                     
16890                     this.store.fireEvent("datachanged", this.store);
16891                     
16892                     this.onLoad();
16893                     
16894                     
16895                 }else{
16896                     
16897                     this.store.baseParams[this.queryParam] = q;
16898                     
16899                     var options = {params : this.getParams(q)};
16900                     
16901                     if(this.loadNext){
16902                         options.add = true;
16903                         options.params.start = this.page * this.pageSize;
16904                     }
16905                     
16906                     this.store.load(options);
16907                     
16908                     /*
16909                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16910                      *  we should expand the list on onLoad
16911                      *  so command out it
16912                      */
16913 //                    this.expand();
16914                 }
16915             }else{
16916                 this.selectedIndex = -1;
16917                 this.onLoad();   
16918             }
16919         }
16920         
16921         this.loadNext = false;
16922     },
16923     
16924     // private
16925     getParams : function(q){
16926         var p = {};
16927         //p[this.queryParam] = q;
16928         
16929         if(this.pageSize){
16930             p.start = 0;
16931             p.limit = this.pageSize;
16932         }
16933         return p;
16934     },
16935
16936     /**
16937      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16938      */
16939     collapse : function(){
16940         if(!this.isExpanded()){
16941             return;
16942         }
16943         
16944         this.list.hide();
16945         
16946         this.hasFocus = false;
16947         
16948         if(this.tickable){
16949             this.okBtn.hide();
16950             this.cancelBtn.hide();
16951             this.trigger.show();
16952             
16953             if(this.editable){
16954                 this.tickableInputEl().dom.value = '';
16955                 this.tickableInputEl().blur();
16956             }
16957             
16958         }
16959         
16960         Roo.get(document).un('mousedown', this.collapseIf, this);
16961         Roo.get(document).un('mousewheel', this.collapseIf, this);
16962         if (!this.editable) {
16963             Roo.get(document).un('keydown', this.listKeyPress, this);
16964         }
16965         this.fireEvent('collapse', this);
16966         
16967         this.validate();
16968     },
16969
16970     // private
16971     collapseIf : function(e){
16972         var in_combo  = e.within(this.el);
16973         var in_list =  e.within(this.list);
16974         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16975         
16976         if (in_combo || in_list || is_list) {
16977             //e.stopPropagation();
16978             return;
16979         }
16980         
16981         if(this.tickable){
16982             this.onTickableFooterButtonClick(e, false, false);
16983         }
16984
16985         this.collapse();
16986         
16987     },
16988
16989     /**
16990      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16991      */
16992     expand : function(){
16993        
16994         if(this.isExpanded() || !this.hasFocus){
16995             return;
16996         }
16997         
16998         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16999         this.list.setWidth(lw);
17000         
17001         Roo.log('expand');
17002         
17003         this.list.show();
17004         
17005         this.restrictHeight();
17006         
17007         if(this.tickable){
17008             
17009             this.tickItems = Roo.apply([], this.item);
17010             
17011             this.okBtn.show();
17012             this.cancelBtn.show();
17013             this.trigger.hide();
17014             
17015             if(this.editable){
17016                 this.tickableInputEl().focus();
17017             }
17018             
17019         }
17020         
17021         Roo.get(document).on('mousedown', this.collapseIf, this);
17022         Roo.get(document).on('mousewheel', this.collapseIf, this);
17023         if (!this.editable) {
17024             Roo.get(document).on('keydown', this.listKeyPress, this);
17025         }
17026         
17027         this.fireEvent('expand', this);
17028     },
17029
17030     // private
17031     // Implements the default empty TriggerField.onTriggerClick function
17032     onTriggerClick : function(e)
17033     {
17034         Roo.log('trigger click');
17035         
17036         if(this.disabled || !this.triggerList){
17037             return;
17038         }
17039         
17040         this.page = 0;
17041         this.loadNext = false;
17042         
17043         if(this.isExpanded()){
17044             this.collapse();
17045             if (!this.blockFocus) {
17046                 this.inputEl().focus();
17047             }
17048             
17049         }else {
17050             this.hasFocus = true;
17051             if(this.triggerAction == 'all') {
17052                 this.doQuery(this.allQuery, true);
17053             } else {
17054                 this.doQuery(this.getRawValue());
17055             }
17056             if (!this.blockFocus) {
17057                 this.inputEl().focus();
17058             }
17059         }
17060     },
17061     
17062     onTickableTriggerClick : function(e)
17063     {
17064         if(this.disabled){
17065             return;
17066         }
17067         
17068         this.page = 0;
17069         this.loadNext = false;
17070         this.hasFocus = true;
17071         
17072         if(this.triggerAction == 'all') {
17073             this.doQuery(this.allQuery, true);
17074         } else {
17075             this.doQuery(this.getRawValue());
17076         }
17077     },
17078     
17079     onSearchFieldClick : function(e)
17080     {
17081         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17082             this.onTickableFooterButtonClick(e, false, false);
17083             return;
17084         }
17085         
17086         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17087             return;
17088         }
17089         
17090         this.page = 0;
17091         this.loadNext = false;
17092         this.hasFocus = true;
17093         
17094         if(this.triggerAction == 'all') {
17095             this.doQuery(this.allQuery, true);
17096         } else {
17097             this.doQuery(this.getRawValue());
17098         }
17099     },
17100     
17101     listKeyPress : function(e)
17102     {
17103         //Roo.log('listkeypress');
17104         // scroll to first matching element based on key pres..
17105         if (e.isSpecialKey()) {
17106             return false;
17107         }
17108         var k = String.fromCharCode(e.getKey()).toUpperCase();
17109         //Roo.log(k);
17110         var match  = false;
17111         var csel = this.view.getSelectedNodes();
17112         var cselitem = false;
17113         if (csel.length) {
17114             var ix = this.view.indexOf(csel[0]);
17115             cselitem  = this.store.getAt(ix);
17116             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17117                 cselitem = false;
17118             }
17119             
17120         }
17121         
17122         this.store.each(function(v) { 
17123             if (cselitem) {
17124                 // start at existing selection.
17125                 if (cselitem.id == v.id) {
17126                     cselitem = false;
17127                 }
17128                 return true;
17129             }
17130                 
17131             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17132                 match = this.store.indexOf(v);
17133                 return false;
17134             }
17135             return true;
17136         }, this);
17137         
17138         if (match === false) {
17139             return true; // no more action?
17140         }
17141         // scroll to?
17142         this.view.select(match);
17143         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17144         sn.scrollIntoView(sn.dom.parentNode, false);
17145     },
17146     
17147     onViewScroll : function(e, t){
17148         
17149         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){
17150             return;
17151         }
17152         
17153         this.hasQuery = true;
17154         
17155         this.loading = this.list.select('.loading', true).first();
17156         
17157         if(this.loading === null){
17158             this.list.createChild({
17159                 tag: 'div',
17160                 cls: 'loading roo-select2-more-results roo-select2-active',
17161                 html: 'Loading more results...'
17162             });
17163             
17164             this.loading = this.list.select('.loading', true).first();
17165             
17166             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17167             
17168             this.loading.hide();
17169         }
17170         
17171         this.loading.show();
17172         
17173         var _combo = this;
17174         
17175         this.page++;
17176         this.loadNext = true;
17177         
17178         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17179         
17180         return;
17181     },
17182     
17183     addItem : function(o)
17184     {   
17185         var dv = ''; // display value
17186         
17187         if (this.displayField) {
17188             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17189         } else {
17190             // this is an error condition!!!
17191             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17192         }
17193         
17194         if(!dv.length){
17195             return;
17196         }
17197         
17198         var choice = this.choices.createChild({
17199             tag: 'li',
17200             cls: 'roo-select2-search-choice',
17201             cn: [
17202                 {
17203                     tag: 'div',
17204                     html: dv
17205                 },
17206                 {
17207                     tag: 'a',
17208                     href: '#',
17209                     cls: 'roo-select2-search-choice-close fa fa-times',
17210                     tabindex: '-1'
17211                 }
17212             ]
17213             
17214         }, this.searchField);
17215         
17216         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17217         
17218         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17219         
17220         this.item.push(o);
17221         
17222         this.lastData = o;
17223         
17224         this.syncValue();
17225         
17226         this.inputEl().dom.value = '';
17227         
17228         this.validate();
17229     },
17230     
17231     onRemoveItem : function(e, _self, o)
17232     {
17233         e.preventDefault();
17234         
17235         this.lastItem = Roo.apply([], this.item);
17236         
17237         var index = this.item.indexOf(o.data) * 1;
17238         
17239         if( index < 0){
17240             Roo.log('not this item?!');
17241             return;
17242         }
17243         
17244         this.item.splice(index, 1);
17245         o.item.remove();
17246         
17247         this.syncValue();
17248         
17249         this.fireEvent('remove', this, e);
17250         
17251         this.validate();
17252         
17253     },
17254     
17255     syncValue : function()
17256     {
17257         if(!this.item.length){
17258             this.clearValue();
17259             return;
17260         }
17261             
17262         var value = [];
17263         var _this = this;
17264         Roo.each(this.item, function(i){
17265             if(_this.valueField){
17266                 value.push(i[_this.valueField]);
17267                 return;
17268             }
17269
17270             value.push(i);
17271         });
17272
17273         this.value = value.join(',');
17274
17275         if(this.hiddenField){
17276             this.hiddenField.dom.value = this.value;
17277         }
17278         
17279         this.store.fireEvent("datachanged", this.store);
17280         
17281         this.validate();
17282     },
17283     
17284     clearItem : function()
17285     {
17286         if(!this.multiple){
17287             return;
17288         }
17289         
17290         this.item = [];
17291         
17292         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17293            c.remove();
17294         });
17295         
17296         this.syncValue();
17297         
17298         this.validate();
17299         
17300         if(this.tickable && !Roo.isTouch){
17301             this.view.refresh();
17302         }
17303     },
17304     
17305     inputEl: function ()
17306     {
17307         if(Roo.isIOS && this.useNativeIOS){
17308             return this.el.select('select.roo-ios-select', true).first();
17309         }
17310         
17311         if(Roo.isTouch && this.mobileTouchView){
17312             return this.el.select('input.form-control',true).first();
17313         }
17314         
17315         if(this.tickable){
17316             return this.searchField;
17317         }
17318         
17319         return this.el.select('input.form-control',true).first();
17320     },
17321     
17322     onTickableFooterButtonClick : function(e, btn, el)
17323     {
17324         e.preventDefault();
17325         
17326         this.lastItem = Roo.apply([], this.item);
17327         
17328         if(btn && btn.name == 'cancel'){
17329             this.tickItems = Roo.apply([], this.item);
17330             this.collapse();
17331             return;
17332         }
17333         
17334         this.clearItem();
17335         
17336         var _this = this;
17337         
17338         Roo.each(this.tickItems, function(o){
17339             _this.addItem(o);
17340         });
17341         
17342         this.collapse();
17343         
17344     },
17345     
17346     validate : function()
17347     {
17348         if(this.getVisibilityEl().hasClass('hidden')){
17349             return true;
17350         }
17351         
17352         var v = this.getRawValue();
17353         
17354         if(this.multiple){
17355             v = this.getValue();
17356         }
17357         
17358         if(this.disabled || this.allowBlank || v.length){
17359             this.markValid();
17360             return true;
17361         }
17362         
17363         this.markInvalid();
17364         return false;
17365     },
17366     
17367     tickableInputEl : function()
17368     {
17369         if(!this.tickable || !this.editable){
17370             return this.inputEl();
17371         }
17372         
17373         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17374     },
17375     
17376     
17377     getAutoCreateTouchView : function()
17378     {
17379         var id = Roo.id();
17380         
17381         var cfg = {
17382             cls: 'form-group' //input-group
17383         };
17384         
17385         var input =  {
17386             tag: 'input',
17387             id : id,
17388             type : this.inputType,
17389             cls : 'form-control x-combo-noedit',
17390             autocomplete: 'new-password',
17391             placeholder : this.placeholder || '',
17392             readonly : true
17393         };
17394         
17395         if (this.name) {
17396             input.name = this.name;
17397         }
17398         
17399         if (this.size) {
17400             input.cls += ' input-' + this.size;
17401         }
17402         
17403         if (this.disabled) {
17404             input.disabled = true;
17405         }
17406         
17407         var inputblock = {
17408             cls : 'roo-combobox-wrap',
17409             cn : [
17410                 input
17411             ]
17412         };
17413         
17414         if(this.before){
17415             inputblock.cls += ' input-group';
17416             
17417             inputblock.cn.unshift({
17418                 tag :'span',
17419                 cls : 'input-group-addon input-group-prepend input-group-text',
17420                 html : this.before
17421             });
17422         }
17423         
17424         if(this.removable && !this.multiple){
17425             inputblock.cls += ' roo-removable';
17426             
17427             inputblock.cn.push({
17428                 tag: 'button',
17429                 html : 'x',
17430                 cls : 'roo-combo-removable-btn close'
17431             });
17432         }
17433
17434         if(this.hasFeedback && !this.allowBlank){
17435             
17436             inputblock.cls += ' has-feedback';
17437             
17438             inputblock.cn.push({
17439                 tag: 'span',
17440                 cls: 'glyphicon form-control-feedback'
17441             });
17442             
17443         }
17444         
17445         if (this.after) {
17446             
17447             inputblock.cls += (this.before) ? '' : ' input-group';
17448             
17449             inputblock.cn.push({
17450                 tag :'span',
17451                 cls : 'input-group-addon input-group-append input-group-text',
17452                 html : this.after
17453             });
17454         }
17455
17456         
17457         var ibwrap = inputblock;
17458         
17459         if(this.multiple){
17460             ibwrap = {
17461                 tag: 'ul',
17462                 cls: 'roo-select2-choices',
17463                 cn:[
17464                     {
17465                         tag: 'li',
17466                         cls: 'roo-select2-search-field',
17467                         cn: [
17468
17469                             inputblock
17470                         ]
17471                     }
17472                 ]
17473             };
17474         
17475             
17476         }
17477         
17478         var combobox = {
17479             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17480             cn: [
17481                 {
17482                     tag: 'input',
17483                     type : 'hidden',
17484                     cls: 'form-hidden-field'
17485                 },
17486                 ibwrap
17487             ]
17488         };
17489         
17490         if(!this.multiple && this.showToggleBtn){
17491             
17492             var caret = {
17493                 cls: 'caret'
17494             };
17495             
17496             if (this.caret != false) {
17497                 caret = {
17498                      tag: 'i',
17499                      cls: 'fa fa-' + this.caret
17500                 };
17501                 
17502             }
17503             
17504             combobox.cn.push({
17505                 tag :'span',
17506                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17507                 cn : [
17508                     Roo.bootstrap.version == 3 ? caret : '',
17509                     {
17510                         tag: 'span',
17511                         cls: 'combobox-clear',
17512                         cn  : [
17513                             {
17514                                 tag : 'i',
17515                                 cls: 'icon-remove'
17516                             }
17517                         ]
17518                     }
17519                 ]
17520
17521             })
17522         }
17523         
17524         if(this.multiple){
17525             combobox.cls += ' roo-select2-container-multi';
17526         }
17527         
17528         var align = this.labelAlign || this.parentLabelAlign();
17529         
17530         if (align ==='left' && this.fieldLabel.length) {
17531
17532             cfg.cn = [
17533                 {
17534                    tag : 'i',
17535                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17536                    tooltip : 'This field is required'
17537                 },
17538                 {
17539                     tag: 'label',
17540                     cls : 'control-label col-form-label',
17541                     html : this.fieldLabel
17542
17543                 },
17544                 {
17545                     cls : 'roo-combobox-wrap ', 
17546                     cn: [
17547                         combobox
17548                     ]
17549                 }
17550             ];
17551             
17552             var labelCfg = cfg.cn[1];
17553             var contentCfg = cfg.cn[2];
17554             
17555
17556             if(this.indicatorpos == 'right'){
17557                 cfg.cn = [
17558                     {
17559                         tag: 'label',
17560                         'for' :  id,
17561                         cls : 'control-label col-form-label',
17562                         cn : [
17563                             {
17564                                 tag : 'span',
17565                                 html : this.fieldLabel
17566                             },
17567                             {
17568                                 tag : 'i',
17569                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17570                                 tooltip : 'This field is required'
17571                             }
17572                         ]
17573                     },
17574                     {
17575                         cls : "roo-combobox-wrap ",
17576                         cn: [
17577                             combobox
17578                         ]
17579                     }
17580
17581                 ];
17582                 
17583                 labelCfg = cfg.cn[0];
17584                 contentCfg = cfg.cn[1];
17585             }
17586             
17587            
17588             
17589             if(this.labelWidth > 12){
17590                 labelCfg.style = "width: " + this.labelWidth + 'px';
17591             }
17592            
17593             if(this.labelWidth < 13 && this.labelmd == 0){
17594                 this.labelmd = this.labelWidth;
17595             }
17596             
17597             if(this.labellg > 0){
17598                 labelCfg.cls += ' col-lg-' + this.labellg;
17599                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17600             }
17601             
17602             if(this.labelmd > 0){
17603                 labelCfg.cls += ' col-md-' + this.labelmd;
17604                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17605             }
17606             
17607             if(this.labelsm > 0){
17608                 labelCfg.cls += ' col-sm-' + this.labelsm;
17609                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17610             }
17611             
17612             if(this.labelxs > 0){
17613                 labelCfg.cls += ' col-xs-' + this.labelxs;
17614                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17615             }
17616                 
17617                 
17618         } else if ( this.fieldLabel.length) {
17619             cfg.cn = [
17620                 {
17621                    tag : 'i',
17622                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17623                    tooltip : 'This field is required'
17624                 },
17625                 {
17626                     tag: 'label',
17627                     cls : 'control-label',
17628                     html : this.fieldLabel
17629
17630                 },
17631                 {
17632                     cls : '', 
17633                     cn: [
17634                         combobox
17635                     ]
17636                 }
17637             ];
17638             
17639             if(this.indicatorpos == 'right'){
17640                 cfg.cn = [
17641                     {
17642                         tag: 'label',
17643                         cls : 'control-label',
17644                         html : this.fieldLabel,
17645                         cn : [
17646                             {
17647                                tag : 'i',
17648                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17649                                tooltip : 'This field is required'
17650                             }
17651                         ]
17652                     },
17653                     {
17654                         cls : '', 
17655                         cn: [
17656                             combobox
17657                         ]
17658                     }
17659                 ];
17660             }
17661         } else {
17662             cfg.cn = combobox;    
17663         }
17664         
17665         
17666         var settings = this;
17667         
17668         ['xs','sm','md','lg'].map(function(size){
17669             if (settings[size]) {
17670                 cfg.cls += ' col-' + size + '-' + settings[size];
17671             }
17672         });
17673         
17674         return cfg;
17675     },
17676     
17677     initTouchView : function()
17678     {
17679         this.renderTouchView();
17680         
17681         this.touchViewEl.on('scroll', function(){
17682             this.el.dom.scrollTop = 0;
17683         }, this);
17684         
17685         this.originalValue = this.getValue();
17686         
17687         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17688         
17689         this.inputEl().on("click", this.showTouchView, this);
17690         if (this.triggerEl) {
17691             this.triggerEl.on("click", this.showTouchView, this);
17692         }
17693         
17694         
17695         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17696         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17697         
17698         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17699         
17700         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17701         this.store.on('load', this.onTouchViewLoad, this);
17702         this.store.on('loadexception', this.onTouchViewLoadException, this);
17703         
17704         if(this.hiddenName){
17705             
17706             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17707             
17708             this.hiddenField.dom.value =
17709                 this.hiddenValue !== undefined ? this.hiddenValue :
17710                 this.value !== undefined ? this.value : '';
17711         
17712             this.el.dom.removeAttribute('name');
17713             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17714         }
17715         
17716         if(this.multiple){
17717             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17718             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17719         }
17720         
17721         if(this.removable && !this.multiple){
17722             var close = this.closeTriggerEl();
17723             if(close){
17724                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17725                 close.on('click', this.removeBtnClick, this, close);
17726             }
17727         }
17728         /*
17729          * fix the bug in Safari iOS8
17730          */
17731         this.inputEl().on("focus", function(e){
17732             document.activeElement.blur();
17733         }, this);
17734         
17735         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17736         
17737         return;
17738         
17739         
17740     },
17741     
17742     renderTouchView : function()
17743     {
17744         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17745         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17746         
17747         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17748         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17749         
17750         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17751         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17752         this.touchViewBodyEl.setStyle('overflow', 'auto');
17753         
17754         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17755         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17756         
17757         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17758         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17759         
17760     },
17761     
17762     showTouchView : function()
17763     {
17764         if(this.disabled){
17765             return;
17766         }
17767         
17768         this.touchViewHeaderEl.hide();
17769
17770         if(this.modalTitle.length){
17771             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17772             this.touchViewHeaderEl.show();
17773         }
17774
17775         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17776         this.touchViewEl.show();
17777
17778         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17779         
17780         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17781         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17782
17783         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17784
17785         if(this.modalTitle.length){
17786             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17787         }
17788         
17789         this.touchViewBodyEl.setHeight(bodyHeight);
17790
17791         if(this.animate){
17792             var _this = this;
17793             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17794         }else{
17795             this.touchViewEl.addClass(['in','show']);
17796         }
17797         
17798         if(this._touchViewMask){
17799             Roo.get(document.body).addClass("x-body-masked");
17800             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17801             this._touchViewMask.setStyle('z-index', 10000);
17802             this._touchViewMask.addClass('show');
17803         }
17804         
17805         this.doTouchViewQuery();
17806         
17807     },
17808     
17809     hideTouchView : function()
17810     {
17811         this.touchViewEl.removeClass(['in','show']);
17812
17813         if(this.animate){
17814             var _this = this;
17815             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17816         }else{
17817             this.touchViewEl.setStyle('display', 'none');
17818         }
17819         
17820         if(this._touchViewMask){
17821             this._touchViewMask.removeClass('show');
17822             Roo.get(document.body).removeClass("x-body-masked");
17823         }
17824     },
17825     
17826     setTouchViewValue : function()
17827     {
17828         if(this.multiple){
17829             this.clearItem();
17830         
17831             var _this = this;
17832
17833             Roo.each(this.tickItems, function(o){
17834                 this.addItem(o);
17835             }, this);
17836         }
17837         
17838         this.hideTouchView();
17839     },
17840     
17841     doTouchViewQuery : function()
17842     {
17843         var qe = {
17844             query: '',
17845             forceAll: true,
17846             combo: this,
17847             cancel:false
17848         };
17849         
17850         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17851             return false;
17852         }
17853         
17854         if(!this.alwaysQuery || this.mode == 'local'){
17855             this.onTouchViewLoad();
17856             return;
17857         }
17858         
17859         this.store.load();
17860     },
17861     
17862     onTouchViewBeforeLoad : function(combo,opts)
17863     {
17864         return;
17865     },
17866
17867     // private
17868     onTouchViewLoad : function()
17869     {
17870         if(this.store.getCount() < 1){
17871             this.onTouchViewEmptyResults();
17872             return;
17873         }
17874         
17875         this.clearTouchView();
17876         
17877         var rawValue = this.getRawValue();
17878         
17879         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17880         
17881         this.tickItems = [];
17882         
17883         this.store.data.each(function(d, rowIndex){
17884             var row = this.touchViewListGroup.createChild(template);
17885             
17886             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17887                 row.addClass(d.data.cls);
17888             }
17889             
17890             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17891                 var cfg = {
17892                     data : d.data,
17893                     html : d.data[this.displayField]
17894                 };
17895                 
17896                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17897                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17898                 }
17899             }
17900             row.removeClass('selected');
17901             if(!this.multiple && this.valueField &&
17902                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17903             {
17904                 // radio buttons..
17905                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17906                 row.addClass('selected');
17907             }
17908             
17909             if(this.multiple && this.valueField &&
17910                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17911             {
17912                 
17913                 // checkboxes...
17914                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17915                 this.tickItems.push(d.data);
17916             }
17917             
17918             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17919             
17920         }, this);
17921         
17922         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17923         
17924         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17925
17926         if(this.modalTitle.length){
17927             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17928         }
17929
17930         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17931         
17932         if(this.mobile_restrict_height && listHeight < bodyHeight){
17933             this.touchViewBodyEl.setHeight(listHeight);
17934         }
17935         
17936         var _this = this;
17937         
17938         if(firstChecked && listHeight > bodyHeight){
17939             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17940         }
17941         
17942     },
17943     
17944     onTouchViewLoadException : function()
17945     {
17946         this.hideTouchView();
17947     },
17948     
17949     onTouchViewEmptyResults : function()
17950     {
17951         this.clearTouchView();
17952         
17953         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17954         
17955         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17956         
17957     },
17958     
17959     clearTouchView : function()
17960     {
17961         this.touchViewListGroup.dom.innerHTML = '';
17962     },
17963     
17964     onTouchViewClick : function(e, el, o)
17965     {
17966         e.preventDefault();
17967         
17968         var row = o.row;
17969         var rowIndex = o.rowIndex;
17970         
17971         var r = this.store.getAt(rowIndex);
17972         
17973         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17974             
17975             if(!this.multiple){
17976                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17977                     c.dom.removeAttribute('checked');
17978                 }, this);
17979
17980                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17981
17982                 this.setFromData(r.data);
17983
17984                 var close = this.closeTriggerEl();
17985
17986                 if(close){
17987                     close.show();
17988                 }
17989
17990                 this.hideTouchView();
17991
17992                 this.fireEvent('select', this, r, rowIndex);
17993
17994                 return;
17995             }
17996
17997             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17998                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17999                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18000                 return;
18001             }
18002
18003             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18004             this.addItem(r.data);
18005             this.tickItems.push(r.data);
18006         }
18007     },
18008     
18009     getAutoCreateNativeIOS : function()
18010     {
18011         var cfg = {
18012             cls: 'form-group' //input-group,
18013         };
18014         
18015         var combobox =  {
18016             tag: 'select',
18017             cls : 'roo-ios-select'
18018         };
18019         
18020         if (this.name) {
18021             combobox.name = this.name;
18022         }
18023         
18024         if (this.disabled) {
18025             combobox.disabled = true;
18026         }
18027         
18028         var settings = this;
18029         
18030         ['xs','sm','md','lg'].map(function(size){
18031             if (settings[size]) {
18032                 cfg.cls += ' col-' + size + '-' + settings[size];
18033             }
18034         });
18035         
18036         cfg.cn = combobox;
18037         
18038         return cfg;
18039         
18040     },
18041     
18042     initIOSView : function()
18043     {
18044         this.store.on('load', this.onIOSViewLoad, this);
18045         
18046         return;
18047     },
18048     
18049     onIOSViewLoad : function()
18050     {
18051         if(this.store.getCount() < 1){
18052             return;
18053         }
18054         
18055         this.clearIOSView();
18056         
18057         if(this.allowBlank) {
18058             
18059             var default_text = '-- SELECT --';
18060             
18061             if(this.placeholder.length){
18062                 default_text = this.placeholder;
18063             }
18064             
18065             if(this.emptyTitle.length){
18066                 default_text += ' - ' + this.emptyTitle + ' -';
18067             }
18068             
18069             var opt = this.inputEl().createChild({
18070                 tag: 'option',
18071                 value : 0,
18072                 html : default_text
18073             });
18074             
18075             var o = {};
18076             o[this.valueField] = 0;
18077             o[this.displayField] = default_text;
18078             
18079             this.ios_options.push({
18080                 data : o,
18081                 el : opt
18082             });
18083             
18084         }
18085         
18086         this.store.data.each(function(d, rowIndex){
18087             
18088             var html = '';
18089             
18090             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18091                 html = d.data[this.displayField];
18092             }
18093             
18094             var value = '';
18095             
18096             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18097                 value = d.data[this.valueField];
18098             }
18099             
18100             var option = {
18101                 tag: 'option',
18102                 value : value,
18103                 html : html
18104             };
18105             
18106             if(this.value == d.data[this.valueField]){
18107                 option['selected'] = true;
18108             }
18109             
18110             var opt = this.inputEl().createChild(option);
18111             
18112             this.ios_options.push({
18113                 data : d.data,
18114                 el : opt
18115             });
18116             
18117         }, this);
18118         
18119         this.inputEl().on('change', function(){
18120            this.fireEvent('select', this);
18121         }, this);
18122         
18123     },
18124     
18125     clearIOSView: function()
18126     {
18127         this.inputEl().dom.innerHTML = '';
18128         
18129         this.ios_options = [];
18130     },
18131     
18132     setIOSValue: function(v)
18133     {
18134         this.value = v;
18135         
18136         if(!this.ios_options){
18137             return;
18138         }
18139         
18140         Roo.each(this.ios_options, function(opts){
18141            
18142            opts.el.dom.removeAttribute('selected');
18143            
18144            if(opts.data[this.valueField] != v){
18145                return;
18146            }
18147            
18148            opts.el.dom.setAttribute('selected', true);
18149            
18150         }, this);
18151     }
18152
18153     /** 
18154     * @cfg {Boolean} grow 
18155     * @hide 
18156     */
18157     /** 
18158     * @cfg {Number} growMin 
18159     * @hide 
18160     */
18161     /** 
18162     * @cfg {Number} growMax 
18163     * @hide 
18164     */
18165     /**
18166      * @hide
18167      * @method autoSize
18168      */
18169 });
18170
18171 Roo.apply(Roo.bootstrap.ComboBox,  {
18172     
18173     header : {
18174         tag: 'div',
18175         cls: 'modal-header',
18176         cn: [
18177             {
18178                 tag: 'h4',
18179                 cls: 'modal-title'
18180             }
18181         ]
18182     },
18183     
18184     body : {
18185         tag: 'div',
18186         cls: 'modal-body',
18187         cn: [
18188             {
18189                 tag: 'ul',
18190                 cls: 'list-group'
18191             }
18192         ]
18193     },
18194     
18195     listItemRadio : {
18196         tag: 'li',
18197         cls: 'list-group-item',
18198         cn: [
18199             {
18200                 tag: 'span',
18201                 cls: 'roo-combobox-list-group-item-value'
18202             },
18203             {
18204                 tag: 'div',
18205                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18206                 cn: [
18207                     {
18208                         tag: 'input',
18209                         type: 'radio'
18210                     },
18211                     {
18212                         tag: 'label'
18213                     }
18214                 ]
18215             }
18216         ]
18217     },
18218     
18219     listItemCheckbox : {
18220         tag: 'li',
18221         cls: 'list-group-item',
18222         cn: [
18223             {
18224                 tag: 'span',
18225                 cls: 'roo-combobox-list-group-item-value'
18226             },
18227             {
18228                 tag: 'div',
18229                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18230                 cn: [
18231                     {
18232                         tag: 'input',
18233                         type: 'checkbox'
18234                     },
18235                     {
18236                         tag: 'label'
18237                     }
18238                 ]
18239             }
18240         ]
18241     },
18242     
18243     emptyResult : {
18244         tag: 'div',
18245         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18246     },
18247     
18248     footer : {
18249         tag: 'div',
18250         cls: 'modal-footer',
18251         cn: [
18252             {
18253                 tag: 'div',
18254                 cls: 'row',
18255                 cn: [
18256                     {
18257                         tag: 'div',
18258                         cls: 'col-xs-6 text-left',
18259                         cn: {
18260                             tag: 'button',
18261                             cls: 'btn btn-danger roo-touch-view-cancel',
18262                             html: 'Cancel'
18263                         }
18264                     },
18265                     {
18266                         tag: 'div',
18267                         cls: 'col-xs-6 text-right',
18268                         cn: {
18269                             tag: 'button',
18270                             cls: 'btn btn-success roo-touch-view-ok',
18271                             html: 'OK'
18272                         }
18273                     }
18274                 ]
18275             }
18276         ]
18277         
18278     }
18279 });
18280
18281 Roo.apply(Roo.bootstrap.ComboBox,  {
18282     
18283     touchViewTemplate : {
18284         tag: 'div',
18285         cls: 'modal fade roo-combobox-touch-view',
18286         cn: [
18287             {
18288                 tag: 'div',
18289                 cls: 'modal-dialog',
18290                 style : 'position:fixed', // we have to fix position....
18291                 cn: [
18292                     {
18293                         tag: 'div',
18294                         cls: 'modal-content',
18295                         cn: [
18296                             Roo.bootstrap.ComboBox.header,
18297                             Roo.bootstrap.ComboBox.body,
18298                             Roo.bootstrap.ComboBox.footer
18299                         ]
18300                     }
18301                 ]
18302             }
18303         ]
18304     }
18305 });/*
18306  * Based on:
18307  * Ext JS Library 1.1.1
18308  * Copyright(c) 2006-2007, Ext JS, LLC.
18309  *
18310  * Originally Released Under LGPL - original licence link has changed is not relivant.
18311  *
18312  * Fork - LGPL
18313  * <script type="text/javascript">
18314  */
18315
18316 /**
18317  * @class Roo.View
18318  * @extends Roo.util.Observable
18319  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18320  * This class also supports single and multi selection modes. <br>
18321  * Create a data model bound view:
18322  <pre><code>
18323  var store = new Roo.data.Store(...);
18324
18325  var view = new Roo.View({
18326     el : "my-element",
18327     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18328  
18329     singleSelect: true,
18330     selectedClass: "ydataview-selected",
18331     store: store
18332  });
18333
18334  // listen for node click?
18335  view.on("click", function(vw, index, node, e){
18336  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18337  });
18338
18339  // load XML data
18340  dataModel.load("foobar.xml");
18341  </code></pre>
18342  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18343  * <br><br>
18344  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18345  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18346  * 
18347  * Note: old style constructor is still suported (container, template, config)
18348  * 
18349  * @constructor
18350  * Create a new View
18351  * @param {Object} config The config object
18352  * 
18353  */
18354 Roo.View = function(config, depreciated_tpl, depreciated_config){
18355     
18356     this.parent = false;
18357     
18358     if (typeof(depreciated_tpl) == 'undefined') {
18359         // new way.. - universal constructor.
18360         Roo.apply(this, config);
18361         this.el  = Roo.get(this.el);
18362     } else {
18363         // old format..
18364         this.el  = Roo.get(config);
18365         this.tpl = depreciated_tpl;
18366         Roo.apply(this, depreciated_config);
18367     }
18368     this.wrapEl  = this.el.wrap().wrap();
18369     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18370     
18371     
18372     if(typeof(this.tpl) == "string"){
18373         this.tpl = new Roo.Template(this.tpl);
18374     } else {
18375         // support xtype ctors..
18376         this.tpl = new Roo.factory(this.tpl, Roo);
18377     }
18378     
18379     
18380     this.tpl.compile();
18381     
18382     /** @private */
18383     this.addEvents({
18384         /**
18385          * @event beforeclick
18386          * Fires before a click is processed. Returns false to cancel the default action.
18387          * @param {Roo.View} this
18388          * @param {Number} index The index of the target node
18389          * @param {HTMLElement} node The target node
18390          * @param {Roo.EventObject} e The raw event object
18391          */
18392             "beforeclick" : true,
18393         /**
18394          * @event click
18395          * Fires when a template node is clicked.
18396          * @param {Roo.View} this
18397          * @param {Number} index The index of the target node
18398          * @param {HTMLElement} node The target node
18399          * @param {Roo.EventObject} e The raw event object
18400          */
18401             "click" : true,
18402         /**
18403          * @event dblclick
18404          * Fires when a template node is double clicked.
18405          * @param {Roo.View} this
18406          * @param {Number} index The index of the target node
18407          * @param {HTMLElement} node The target node
18408          * @param {Roo.EventObject} e The raw event object
18409          */
18410             "dblclick" : true,
18411         /**
18412          * @event contextmenu
18413          * Fires when a template node is right clicked.
18414          * @param {Roo.View} this
18415          * @param {Number} index The index of the target node
18416          * @param {HTMLElement} node The target node
18417          * @param {Roo.EventObject} e The raw event object
18418          */
18419             "contextmenu" : true,
18420         /**
18421          * @event selectionchange
18422          * Fires when the selected nodes change.
18423          * @param {Roo.View} this
18424          * @param {Array} selections Array of the selected nodes
18425          */
18426             "selectionchange" : true,
18427     
18428         /**
18429          * @event beforeselect
18430          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18431          * @param {Roo.View} this
18432          * @param {HTMLElement} node The node to be selected
18433          * @param {Array} selections Array of currently selected nodes
18434          */
18435             "beforeselect" : true,
18436         /**
18437          * @event preparedata
18438          * Fires on every row to render, to allow you to change the data.
18439          * @param {Roo.View} this
18440          * @param {Object} data to be rendered (change this)
18441          */
18442           "preparedata" : true
18443           
18444           
18445         });
18446
18447
18448
18449     this.el.on({
18450         "click": this.onClick,
18451         "dblclick": this.onDblClick,
18452         "contextmenu": this.onContextMenu,
18453         scope:this
18454     });
18455
18456     this.selections = [];
18457     this.nodes = [];
18458     this.cmp = new Roo.CompositeElementLite([]);
18459     if(this.store){
18460         this.store = Roo.factory(this.store, Roo.data);
18461         this.setStore(this.store, true);
18462     }
18463     
18464     if ( this.footer && this.footer.xtype) {
18465            
18466          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18467         
18468         this.footer.dataSource = this.store;
18469         this.footer.container = fctr;
18470         this.footer = Roo.factory(this.footer, Roo);
18471         fctr.insertFirst(this.el);
18472         
18473         // this is a bit insane - as the paging toolbar seems to detach the el..
18474 //        dom.parentNode.parentNode.parentNode
18475          // they get detached?
18476     }
18477     
18478     
18479     Roo.View.superclass.constructor.call(this);
18480     
18481     
18482 };
18483
18484 Roo.extend(Roo.View, Roo.util.Observable, {
18485     
18486      /**
18487      * @cfg {Roo.data.Store} store Data store to load data from.
18488      */
18489     store : false,
18490     
18491     /**
18492      * @cfg {String|Roo.Element} el The container element.
18493      */
18494     el : '',
18495     
18496     /**
18497      * @cfg {String|Roo.Template} tpl The template used by this View 
18498      */
18499     tpl : false,
18500     /**
18501      * @cfg {String} dataName the named area of the template to use as the data area
18502      *                          Works with domtemplates roo-name="name"
18503      */
18504     dataName: false,
18505     /**
18506      * @cfg {String} selectedClass The css class to add to selected nodes
18507      */
18508     selectedClass : "x-view-selected",
18509      /**
18510      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18511      */
18512     emptyText : "",
18513     
18514     /**
18515      * @cfg {String} text to display on mask (default Loading)
18516      */
18517     mask : false,
18518     /**
18519      * @cfg {Boolean} multiSelect Allow multiple selection
18520      */
18521     multiSelect : false,
18522     /**
18523      * @cfg {Boolean} singleSelect Allow single selection
18524      */
18525     singleSelect:  false,
18526     
18527     /**
18528      * @cfg {Boolean} toggleSelect - selecting 
18529      */
18530     toggleSelect : false,
18531     
18532     /**
18533      * @cfg {Boolean} tickable - selecting 
18534      */
18535     tickable : false,
18536     
18537     /**
18538      * Returns the element this view is bound to.
18539      * @return {Roo.Element}
18540      */
18541     getEl : function(){
18542         return this.wrapEl;
18543     },
18544     
18545     
18546
18547     /**
18548      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18549      */
18550     refresh : function(){
18551         //Roo.log('refresh');
18552         var t = this.tpl;
18553         
18554         // if we are using something like 'domtemplate', then
18555         // the what gets used is:
18556         // t.applySubtemplate(NAME, data, wrapping data..)
18557         // the outer template then get' applied with
18558         //     the store 'extra data'
18559         // and the body get's added to the
18560         //      roo-name="data" node?
18561         //      <span class='roo-tpl-{name}'></span> ?????
18562         
18563         
18564         
18565         this.clearSelections();
18566         this.el.update("");
18567         var html = [];
18568         var records = this.store.getRange();
18569         if(records.length < 1) {
18570             
18571             // is this valid??  = should it render a template??
18572             
18573             this.el.update(this.emptyText);
18574             return;
18575         }
18576         var el = this.el;
18577         if (this.dataName) {
18578             this.el.update(t.apply(this.store.meta)); //????
18579             el = this.el.child('.roo-tpl-' + this.dataName);
18580         }
18581         
18582         for(var i = 0, len = records.length; i < len; i++){
18583             var data = this.prepareData(records[i].data, i, records[i]);
18584             this.fireEvent("preparedata", this, data, i, records[i]);
18585             
18586             var d = Roo.apply({}, data);
18587             
18588             if(this.tickable){
18589                 Roo.apply(d, {'roo-id' : Roo.id()});
18590                 
18591                 var _this = this;
18592             
18593                 Roo.each(this.parent.item, function(item){
18594                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18595                         return;
18596                     }
18597                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18598                 });
18599             }
18600             
18601             html[html.length] = Roo.util.Format.trim(
18602                 this.dataName ?
18603                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18604                     t.apply(d)
18605             );
18606         }
18607         
18608         
18609         
18610         el.update(html.join(""));
18611         this.nodes = el.dom.childNodes;
18612         this.updateIndexes(0);
18613     },
18614     
18615
18616     /**
18617      * Function to override to reformat the data that is sent to
18618      * the template for each node.
18619      * DEPRICATED - use the preparedata event handler.
18620      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18621      * a JSON object for an UpdateManager bound view).
18622      */
18623     prepareData : function(data, index, record)
18624     {
18625         this.fireEvent("preparedata", this, data, index, record);
18626         return data;
18627     },
18628
18629     onUpdate : function(ds, record){
18630         // Roo.log('on update');   
18631         this.clearSelections();
18632         var index = this.store.indexOf(record);
18633         var n = this.nodes[index];
18634         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18635         n.parentNode.removeChild(n);
18636         this.updateIndexes(index, index);
18637     },
18638
18639     
18640     
18641 // --------- FIXME     
18642     onAdd : function(ds, records, index)
18643     {
18644         //Roo.log(['on Add', ds, records, index] );        
18645         this.clearSelections();
18646         if(this.nodes.length == 0){
18647             this.refresh();
18648             return;
18649         }
18650         var n = this.nodes[index];
18651         for(var i = 0, len = records.length; i < len; i++){
18652             var d = this.prepareData(records[i].data, i, records[i]);
18653             if(n){
18654                 this.tpl.insertBefore(n, d);
18655             }else{
18656                 
18657                 this.tpl.append(this.el, d);
18658             }
18659         }
18660         this.updateIndexes(index);
18661     },
18662
18663     onRemove : function(ds, record, index){
18664        // Roo.log('onRemove');
18665         this.clearSelections();
18666         var el = this.dataName  ?
18667             this.el.child('.roo-tpl-' + this.dataName) :
18668             this.el; 
18669         
18670         el.dom.removeChild(this.nodes[index]);
18671         this.updateIndexes(index);
18672     },
18673
18674     /**
18675      * Refresh an individual node.
18676      * @param {Number} index
18677      */
18678     refreshNode : function(index){
18679         this.onUpdate(this.store, this.store.getAt(index));
18680     },
18681
18682     updateIndexes : function(startIndex, endIndex){
18683         var ns = this.nodes;
18684         startIndex = startIndex || 0;
18685         endIndex = endIndex || ns.length - 1;
18686         for(var i = startIndex; i <= endIndex; i++){
18687             ns[i].nodeIndex = i;
18688         }
18689     },
18690
18691     /**
18692      * Changes the data store this view uses and refresh the view.
18693      * @param {Store} store
18694      */
18695     setStore : function(store, initial){
18696         if(!initial && this.store){
18697             this.store.un("datachanged", this.refresh);
18698             this.store.un("add", this.onAdd);
18699             this.store.un("remove", this.onRemove);
18700             this.store.un("update", this.onUpdate);
18701             this.store.un("clear", this.refresh);
18702             this.store.un("beforeload", this.onBeforeLoad);
18703             this.store.un("load", this.onLoad);
18704             this.store.un("loadexception", this.onLoad);
18705         }
18706         if(store){
18707           
18708             store.on("datachanged", this.refresh, this);
18709             store.on("add", this.onAdd, this);
18710             store.on("remove", this.onRemove, this);
18711             store.on("update", this.onUpdate, this);
18712             store.on("clear", this.refresh, this);
18713             store.on("beforeload", this.onBeforeLoad, this);
18714             store.on("load", this.onLoad, this);
18715             store.on("loadexception", this.onLoad, this);
18716         }
18717         
18718         if(store){
18719             this.refresh();
18720         }
18721     },
18722     /**
18723      * onbeforeLoad - masks the loading area.
18724      *
18725      */
18726     onBeforeLoad : function(store,opts)
18727     {
18728          //Roo.log('onBeforeLoad');   
18729         if (!opts.add) {
18730             this.el.update("");
18731         }
18732         this.el.mask(this.mask ? this.mask : "Loading" ); 
18733     },
18734     onLoad : function ()
18735     {
18736         this.el.unmask();
18737     },
18738     
18739
18740     /**
18741      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18742      * @param {HTMLElement} node
18743      * @return {HTMLElement} The template node
18744      */
18745     findItemFromChild : function(node){
18746         var el = this.dataName  ?
18747             this.el.child('.roo-tpl-' + this.dataName,true) :
18748             this.el.dom; 
18749         
18750         if(!node || node.parentNode == el){
18751                     return node;
18752             }
18753             var p = node.parentNode;
18754             while(p && p != el){
18755             if(p.parentNode == el){
18756                 return p;
18757             }
18758             p = p.parentNode;
18759         }
18760             return null;
18761     },
18762
18763     /** @ignore */
18764     onClick : function(e){
18765         var item = this.findItemFromChild(e.getTarget());
18766         if(item){
18767             var index = this.indexOf(item);
18768             if(this.onItemClick(item, index, e) !== false){
18769                 this.fireEvent("click", this, index, item, e);
18770             }
18771         }else{
18772             this.clearSelections();
18773         }
18774     },
18775
18776     /** @ignore */
18777     onContextMenu : function(e){
18778         var item = this.findItemFromChild(e.getTarget());
18779         if(item){
18780             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18781         }
18782     },
18783
18784     /** @ignore */
18785     onDblClick : function(e){
18786         var item = this.findItemFromChild(e.getTarget());
18787         if(item){
18788             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18789         }
18790     },
18791
18792     onItemClick : function(item, index, e)
18793     {
18794         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18795             return false;
18796         }
18797         if (this.toggleSelect) {
18798             var m = this.isSelected(item) ? 'unselect' : 'select';
18799             //Roo.log(m);
18800             var _t = this;
18801             _t[m](item, true, false);
18802             return true;
18803         }
18804         if(this.multiSelect || this.singleSelect){
18805             if(this.multiSelect && e.shiftKey && this.lastSelection){
18806                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18807             }else{
18808                 this.select(item, this.multiSelect && e.ctrlKey);
18809                 this.lastSelection = item;
18810             }
18811             
18812             if(!this.tickable){
18813                 e.preventDefault();
18814             }
18815             
18816         }
18817         return true;
18818     },
18819
18820     /**
18821      * Get the number of selected nodes.
18822      * @return {Number}
18823      */
18824     getSelectionCount : function(){
18825         return this.selections.length;
18826     },
18827
18828     /**
18829      * Get the currently selected nodes.
18830      * @return {Array} An array of HTMLElements
18831      */
18832     getSelectedNodes : function(){
18833         return this.selections;
18834     },
18835
18836     /**
18837      * Get the indexes of the selected nodes.
18838      * @return {Array}
18839      */
18840     getSelectedIndexes : function(){
18841         var indexes = [], s = this.selections;
18842         for(var i = 0, len = s.length; i < len; i++){
18843             indexes.push(s[i].nodeIndex);
18844         }
18845         return indexes;
18846     },
18847
18848     /**
18849      * Clear all selections
18850      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18851      */
18852     clearSelections : function(suppressEvent){
18853         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18854             this.cmp.elements = this.selections;
18855             this.cmp.removeClass(this.selectedClass);
18856             this.selections = [];
18857             if(!suppressEvent){
18858                 this.fireEvent("selectionchange", this, this.selections);
18859             }
18860         }
18861     },
18862
18863     /**
18864      * Returns true if the passed node is selected
18865      * @param {HTMLElement/Number} node The node or node index
18866      * @return {Boolean}
18867      */
18868     isSelected : function(node){
18869         var s = this.selections;
18870         if(s.length < 1){
18871             return false;
18872         }
18873         node = this.getNode(node);
18874         return s.indexOf(node) !== -1;
18875     },
18876
18877     /**
18878      * Selects nodes.
18879      * @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
18880      * @param {Boolean} keepExisting (optional) true to keep existing selections
18881      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18882      */
18883     select : function(nodeInfo, keepExisting, suppressEvent){
18884         if(nodeInfo instanceof Array){
18885             if(!keepExisting){
18886                 this.clearSelections(true);
18887             }
18888             for(var i = 0, len = nodeInfo.length; i < len; i++){
18889                 this.select(nodeInfo[i], true, true);
18890             }
18891             return;
18892         } 
18893         var node = this.getNode(nodeInfo);
18894         if(!node || this.isSelected(node)){
18895             return; // already selected.
18896         }
18897         if(!keepExisting){
18898             this.clearSelections(true);
18899         }
18900         
18901         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18902             Roo.fly(node).addClass(this.selectedClass);
18903             this.selections.push(node);
18904             if(!suppressEvent){
18905                 this.fireEvent("selectionchange", this, this.selections);
18906             }
18907         }
18908         
18909         
18910     },
18911       /**
18912      * Unselects nodes.
18913      * @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
18914      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18915      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18916      */
18917     unselect : function(nodeInfo, keepExisting, suppressEvent)
18918     {
18919         if(nodeInfo instanceof Array){
18920             Roo.each(this.selections, function(s) {
18921                 this.unselect(s, nodeInfo);
18922             }, this);
18923             return;
18924         }
18925         var node = this.getNode(nodeInfo);
18926         if(!node || !this.isSelected(node)){
18927             //Roo.log("not selected");
18928             return; // not selected.
18929         }
18930         // fireevent???
18931         var ns = [];
18932         Roo.each(this.selections, function(s) {
18933             if (s == node ) {
18934                 Roo.fly(node).removeClass(this.selectedClass);
18935
18936                 return;
18937             }
18938             ns.push(s);
18939         },this);
18940         
18941         this.selections= ns;
18942         this.fireEvent("selectionchange", this, this.selections);
18943     },
18944
18945     /**
18946      * Gets a template node.
18947      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18948      * @return {HTMLElement} The node or null if it wasn't found
18949      */
18950     getNode : function(nodeInfo){
18951         if(typeof nodeInfo == "string"){
18952             return document.getElementById(nodeInfo);
18953         }else if(typeof nodeInfo == "number"){
18954             return this.nodes[nodeInfo];
18955         }
18956         return nodeInfo;
18957     },
18958
18959     /**
18960      * Gets a range template nodes.
18961      * @param {Number} startIndex
18962      * @param {Number} endIndex
18963      * @return {Array} An array of nodes
18964      */
18965     getNodes : function(start, end){
18966         var ns = this.nodes;
18967         start = start || 0;
18968         end = typeof end == "undefined" ? ns.length - 1 : end;
18969         var nodes = [];
18970         if(start <= end){
18971             for(var i = start; i <= end; i++){
18972                 nodes.push(ns[i]);
18973             }
18974         } else{
18975             for(var i = start; i >= end; i--){
18976                 nodes.push(ns[i]);
18977             }
18978         }
18979         return nodes;
18980     },
18981
18982     /**
18983      * Finds the index of the passed node
18984      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18985      * @return {Number} The index of the node or -1
18986      */
18987     indexOf : function(node){
18988         node = this.getNode(node);
18989         if(typeof node.nodeIndex == "number"){
18990             return node.nodeIndex;
18991         }
18992         var ns = this.nodes;
18993         for(var i = 0, len = ns.length; i < len; i++){
18994             if(ns[i] == node){
18995                 return i;
18996             }
18997         }
18998         return -1;
18999     }
19000 });
19001 /*
19002  * - LGPL
19003  *
19004  * based on jquery fullcalendar
19005  * 
19006  */
19007
19008 Roo.bootstrap = Roo.bootstrap || {};
19009 /**
19010  * @class Roo.bootstrap.Calendar
19011  * @extends Roo.bootstrap.Component
19012  * Bootstrap Calendar class
19013  * @cfg {Boolean} loadMask (true|false) default false
19014  * @cfg {Object} header generate the user specific header of the calendar, default false
19015
19016  * @constructor
19017  * Create a new Container
19018  * @param {Object} config The config object
19019  */
19020
19021
19022
19023 Roo.bootstrap.Calendar = function(config){
19024     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19025      this.addEvents({
19026         /**
19027              * @event select
19028              * Fires when a date is selected
19029              * @param {DatePicker} this
19030              * @param {Date} date The selected date
19031              */
19032         'select': true,
19033         /**
19034              * @event monthchange
19035              * Fires when the displayed month changes 
19036              * @param {DatePicker} this
19037              * @param {Date} date The selected month
19038              */
19039         'monthchange': true,
19040         /**
19041              * @event evententer
19042              * Fires when mouse over an event
19043              * @param {Calendar} this
19044              * @param {event} Event
19045              */
19046         'evententer': true,
19047         /**
19048              * @event eventleave
19049              * Fires when the mouse leaves an
19050              * @param {Calendar} this
19051              * @param {event}
19052              */
19053         'eventleave': true,
19054         /**
19055              * @event eventclick
19056              * Fires when the mouse click an
19057              * @param {Calendar} this
19058              * @param {event}
19059              */
19060         'eventclick': true
19061         
19062     });
19063
19064 };
19065
19066 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19067     
19068      /**
19069      * @cfg {Number} startDay
19070      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19071      */
19072     startDay : 0,
19073     
19074     loadMask : false,
19075     
19076     header : false,
19077       
19078     getAutoCreate : function(){
19079         
19080         
19081         var fc_button = function(name, corner, style, content ) {
19082             return Roo.apply({},{
19083                 tag : 'span',
19084                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19085                          (corner.length ?
19086                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19087                             ''
19088                         ),
19089                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19090                 unselectable: 'on'
19091             });
19092         };
19093         
19094         var header = {};
19095         
19096         if(!this.header){
19097             header = {
19098                 tag : 'table',
19099                 cls : 'fc-header',
19100                 style : 'width:100%',
19101                 cn : [
19102                     {
19103                         tag: 'tr',
19104                         cn : [
19105                             {
19106                                 tag : 'td',
19107                                 cls : 'fc-header-left',
19108                                 cn : [
19109                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19110                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19111                                     { tag: 'span', cls: 'fc-header-space' },
19112                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19113
19114
19115                                 ]
19116                             },
19117
19118                             {
19119                                 tag : 'td',
19120                                 cls : 'fc-header-center',
19121                                 cn : [
19122                                     {
19123                                         tag: 'span',
19124                                         cls: 'fc-header-title',
19125                                         cn : {
19126                                             tag: 'H2',
19127                                             html : 'month / year'
19128                                         }
19129                                     }
19130
19131                                 ]
19132                             },
19133                             {
19134                                 tag : 'td',
19135                                 cls : 'fc-header-right',
19136                                 cn : [
19137                               /*      fc_button('month', 'left', '', 'month' ),
19138                                     fc_button('week', '', '', 'week' ),
19139                                     fc_button('day', 'right', '', 'day' )
19140                                 */    
19141
19142                                 ]
19143                             }
19144
19145                         ]
19146                     }
19147                 ]
19148             };
19149         }
19150         
19151         header = this.header;
19152         
19153        
19154         var cal_heads = function() {
19155             var ret = [];
19156             // fixme - handle this.
19157             
19158             for (var i =0; i < Date.dayNames.length; i++) {
19159                 var d = Date.dayNames[i];
19160                 ret.push({
19161                     tag: 'th',
19162                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19163                     html : d.substring(0,3)
19164                 });
19165                 
19166             }
19167             ret[0].cls += ' fc-first';
19168             ret[6].cls += ' fc-last';
19169             return ret;
19170         };
19171         var cal_cell = function(n) {
19172             return  {
19173                 tag: 'td',
19174                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19175                 cn : [
19176                     {
19177                         cn : [
19178                             {
19179                                 cls: 'fc-day-number',
19180                                 html: 'D'
19181                             },
19182                             {
19183                                 cls: 'fc-day-content',
19184                              
19185                                 cn : [
19186                                      {
19187                                         style: 'position: relative;' // height: 17px;
19188                                     }
19189                                 ]
19190                             }
19191                             
19192                             
19193                         ]
19194                     }
19195                 ]
19196                 
19197             }
19198         };
19199         var cal_rows = function() {
19200             
19201             var ret = [];
19202             for (var r = 0; r < 6; r++) {
19203                 var row= {
19204                     tag : 'tr',
19205                     cls : 'fc-week',
19206                     cn : []
19207                 };
19208                 
19209                 for (var i =0; i < Date.dayNames.length; i++) {
19210                     var d = Date.dayNames[i];
19211                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19212
19213                 }
19214                 row.cn[0].cls+=' fc-first';
19215                 row.cn[0].cn[0].style = 'min-height:90px';
19216                 row.cn[6].cls+=' fc-last';
19217                 ret.push(row);
19218                 
19219             }
19220             ret[0].cls += ' fc-first';
19221             ret[4].cls += ' fc-prev-last';
19222             ret[5].cls += ' fc-last';
19223             return ret;
19224             
19225         };
19226         
19227         var cal_table = {
19228             tag: 'table',
19229             cls: 'fc-border-separate',
19230             style : 'width:100%',
19231             cellspacing  : 0,
19232             cn : [
19233                 { 
19234                     tag: 'thead',
19235                     cn : [
19236                         { 
19237                             tag: 'tr',
19238                             cls : 'fc-first fc-last',
19239                             cn : cal_heads()
19240                         }
19241                     ]
19242                 },
19243                 { 
19244                     tag: 'tbody',
19245                     cn : cal_rows()
19246                 }
19247                   
19248             ]
19249         };
19250          
19251          var cfg = {
19252             cls : 'fc fc-ltr',
19253             cn : [
19254                 header,
19255                 {
19256                     cls : 'fc-content',
19257                     style : "position: relative;",
19258                     cn : [
19259                         {
19260                             cls : 'fc-view fc-view-month fc-grid',
19261                             style : 'position: relative',
19262                             unselectable : 'on',
19263                             cn : [
19264                                 {
19265                                     cls : 'fc-event-container',
19266                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19267                                 },
19268                                 cal_table
19269                             ]
19270                         }
19271                     ]
19272     
19273                 }
19274            ] 
19275             
19276         };
19277         
19278          
19279         
19280         return cfg;
19281     },
19282     
19283     
19284     initEvents : function()
19285     {
19286         if(!this.store){
19287             throw "can not find store for calendar";
19288         }
19289         
19290         var mark = {
19291             tag: "div",
19292             cls:"x-dlg-mask",
19293             style: "text-align:center",
19294             cn: [
19295                 {
19296                     tag: "div",
19297                     style: "background-color:white;width:50%;margin:250 auto",
19298                     cn: [
19299                         {
19300                             tag: "img",
19301                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19302                         },
19303                         {
19304                             tag: "span",
19305                             html: "Loading"
19306                         }
19307                         
19308                     ]
19309                 }
19310             ]
19311         };
19312         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19313         
19314         var size = this.el.select('.fc-content', true).first().getSize();
19315         this.maskEl.setSize(size.width, size.height);
19316         this.maskEl.enableDisplayMode("block");
19317         if(!this.loadMask){
19318             this.maskEl.hide();
19319         }
19320         
19321         this.store = Roo.factory(this.store, Roo.data);
19322         this.store.on('load', this.onLoad, this);
19323         this.store.on('beforeload', this.onBeforeLoad, this);
19324         
19325         this.resize();
19326         
19327         this.cells = this.el.select('.fc-day',true);
19328         //Roo.log(this.cells);
19329         this.textNodes = this.el.query('.fc-day-number');
19330         this.cells.addClassOnOver('fc-state-hover');
19331         
19332         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19333         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19334         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19335         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19336         
19337         this.on('monthchange', this.onMonthChange, this);
19338         
19339         this.update(new Date().clearTime());
19340     },
19341     
19342     resize : function() {
19343         var sz  = this.el.getSize();
19344         
19345         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19346         this.el.select('.fc-day-content div',true).setHeight(34);
19347     },
19348     
19349     
19350     // private
19351     showPrevMonth : function(e){
19352         this.update(this.activeDate.add("mo", -1));
19353     },
19354     showToday : function(e){
19355         this.update(new Date().clearTime());
19356     },
19357     // private
19358     showNextMonth : function(e){
19359         this.update(this.activeDate.add("mo", 1));
19360     },
19361
19362     // private
19363     showPrevYear : function(){
19364         this.update(this.activeDate.add("y", -1));
19365     },
19366
19367     // private
19368     showNextYear : function(){
19369         this.update(this.activeDate.add("y", 1));
19370     },
19371
19372     
19373    // private
19374     update : function(date)
19375     {
19376         var vd = this.activeDate;
19377         this.activeDate = date;
19378 //        if(vd && this.el){
19379 //            var t = date.getTime();
19380 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19381 //                Roo.log('using add remove');
19382 //                
19383 //                this.fireEvent('monthchange', this, date);
19384 //                
19385 //                this.cells.removeClass("fc-state-highlight");
19386 //                this.cells.each(function(c){
19387 //                   if(c.dateValue == t){
19388 //                       c.addClass("fc-state-highlight");
19389 //                       setTimeout(function(){
19390 //                            try{c.dom.firstChild.focus();}catch(e){}
19391 //                       }, 50);
19392 //                       return false;
19393 //                   }
19394 //                   return true;
19395 //                });
19396 //                return;
19397 //            }
19398 //        }
19399         
19400         var days = date.getDaysInMonth();
19401         
19402         var firstOfMonth = date.getFirstDateOfMonth();
19403         var startingPos = firstOfMonth.getDay()-this.startDay;
19404         
19405         if(startingPos < this.startDay){
19406             startingPos += 7;
19407         }
19408         
19409         var pm = date.add(Date.MONTH, -1);
19410         var prevStart = pm.getDaysInMonth()-startingPos;
19411 //        
19412         this.cells = this.el.select('.fc-day',true);
19413         this.textNodes = this.el.query('.fc-day-number');
19414         this.cells.addClassOnOver('fc-state-hover');
19415         
19416         var cells = this.cells.elements;
19417         var textEls = this.textNodes;
19418         
19419         Roo.each(cells, function(cell){
19420             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19421         });
19422         
19423         days += startingPos;
19424
19425         // convert everything to numbers so it's fast
19426         var day = 86400000;
19427         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19428         //Roo.log(d);
19429         //Roo.log(pm);
19430         //Roo.log(prevStart);
19431         
19432         var today = new Date().clearTime().getTime();
19433         var sel = date.clearTime().getTime();
19434         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19435         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19436         var ddMatch = this.disabledDatesRE;
19437         var ddText = this.disabledDatesText;
19438         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19439         var ddaysText = this.disabledDaysText;
19440         var format = this.format;
19441         
19442         var setCellClass = function(cal, cell){
19443             cell.row = 0;
19444             cell.events = [];
19445             cell.more = [];
19446             //Roo.log('set Cell Class');
19447             cell.title = "";
19448             var t = d.getTime();
19449             
19450             //Roo.log(d);
19451             
19452             cell.dateValue = t;
19453             if(t == today){
19454                 cell.className += " fc-today";
19455                 cell.className += " fc-state-highlight";
19456                 cell.title = cal.todayText;
19457             }
19458             if(t == sel){
19459                 // disable highlight in other month..
19460                 //cell.className += " fc-state-highlight";
19461                 
19462             }
19463             // disabling
19464             if(t < min) {
19465                 cell.className = " fc-state-disabled";
19466                 cell.title = cal.minText;
19467                 return;
19468             }
19469             if(t > max) {
19470                 cell.className = " fc-state-disabled";
19471                 cell.title = cal.maxText;
19472                 return;
19473             }
19474             if(ddays){
19475                 if(ddays.indexOf(d.getDay()) != -1){
19476                     cell.title = ddaysText;
19477                     cell.className = " fc-state-disabled";
19478                 }
19479             }
19480             if(ddMatch && format){
19481                 var fvalue = d.dateFormat(format);
19482                 if(ddMatch.test(fvalue)){
19483                     cell.title = ddText.replace("%0", fvalue);
19484                     cell.className = " fc-state-disabled";
19485                 }
19486             }
19487             
19488             if (!cell.initialClassName) {
19489                 cell.initialClassName = cell.dom.className;
19490             }
19491             
19492             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19493         };
19494
19495         var i = 0;
19496         
19497         for(; i < startingPos; i++) {
19498             textEls[i].innerHTML = (++prevStart);
19499             d.setDate(d.getDate()+1);
19500             
19501             cells[i].className = "fc-past fc-other-month";
19502             setCellClass(this, cells[i]);
19503         }
19504         
19505         var intDay = 0;
19506         
19507         for(; i < days; i++){
19508             intDay = i - startingPos + 1;
19509             textEls[i].innerHTML = (intDay);
19510             d.setDate(d.getDate()+1);
19511             
19512             cells[i].className = ''; // "x-date-active";
19513             setCellClass(this, cells[i]);
19514         }
19515         var extraDays = 0;
19516         
19517         for(; i < 42; i++) {
19518             textEls[i].innerHTML = (++extraDays);
19519             d.setDate(d.getDate()+1);
19520             
19521             cells[i].className = "fc-future fc-other-month";
19522             setCellClass(this, cells[i]);
19523         }
19524         
19525         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19526         
19527         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19528         
19529         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19530         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19531         
19532         if(totalRows != 6){
19533             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19534             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19535         }
19536         
19537         this.fireEvent('monthchange', this, date);
19538         
19539         
19540         /*
19541         if(!this.internalRender){
19542             var main = this.el.dom.firstChild;
19543             var w = main.offsetWidth;
19544             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19545             Roo.fly(main).setWidth(w);
19546             this.internalRender = true;
19547             // opera does not respect the auto grow header center column
19548             // then, after it gets a width opera refuses to recalculate
19549             // without a second pass
19550             if(Roo.isOpera && !this.secondPass){
19551                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19552                 this.secondPass = true;
19553                 this.update.defer(10, this, [date]);
19554             }
19555         }
19556         */
19557         
19558     },
19559     
19560     findCell : function(dt) {
19561         dt = dt.clearTime().getTime();
19562         var ret = false;
19563         this.cells.each(function(c){
19564             //Roo.log("check " +c.dateValue + '?=' + dt);
19565             if(c.dateValue == dt){
19566                 ret = c;
19567                 return false;
19568             }
19569             return true;
19570         });
19571         
19572         return ret;
19573     },
19574     
19575     findCells : function(ev) {
19576         var s = ev.start.clone().clearTime().getTime();
19577        // Roo.log(s);
19578         var e= ev.end.clone().clearTime().getTime();
19579        // Roo.log(e);
19580         var ret = [];
19581         this.cells.each(function(c){
19582              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19583             
19584             if(c.dateValue > e){
19585                 return ;
19586             }
19587             if(c.dateValue < s){
19588                 return ;
19589             }
19590             ret.push(c);
19591         });
19592         
19593         return ret;    
19594     },
19595     
19596 //    findBestRow: function(cells)
19597 //    {
19598 //        var ret = 0;
19599 //        
19600 //        for (var i =0 ; i < cells.length;i++) {
19601 //            ret  = Math.max(cells[i].rows || 0,ret);
19602 //        }
19603 //        return ret;
19604 //        
19605 //    },
19606     
19607     
19608     addItem : function(ev)
19609     {
19610         // look for vertical location slot in
19611         var cells = this.findCells(ev);
19612         
19613 //        ev.row = this.findBestRow(cells);
19614         
19615         // work out the location.
19616         
19617         var crow = false;
19618         var rows = [];
19619         for(var i =0; i < cells.length; i++) {
19620             
19621             cells[i].row = cells[0].row;
19622             
19623             if(i == 0){
19624                 cells[i].row = cells[i].row + 1;
19625             }
19626             
19627             if (!crow) {
19628                 crow = {
19629                     start : cells[i],
19630                     end :  cells[i]
19631                 };
19632                 continue;
19633             }
19634             if (crow.start.getY() == cells[i].getY()) {
19635                 // on same row.
19636                 crow.end = cells[i];
19637                 continue;
19638             }
19639             // different row.
19640             rows.push(crow);
19641             crow = {
19642                 start: cells[i],
19643                 end : cells[i]
19644             };
19645             
19646         }
19647         
19648         rows.push(crow);
19649         ev.els = [];
19650         ev.rows = rows;
19651         ev.cells = cells;
19652         
19653         cells[0].events.push(ev);
19654         
19655         this.calevents.push(ev);
19656     },
19657     
19658     clearEvents: function() {
19659         
19660         if(!this.calevents){
19661             return;
19662         }
19663         
19664         Roo.each(this.cells.elements, function(c){
19665             c.row = 0;
19666             c.events = [];
19667             c.more = [];
19668         });
19669         
19670         Roo.each(this.calevents, function(e) {
19671             Roo.each(e.els, function(el) {
19672                 el.un('mouseenter' ,this.onEventEnter, this);
19673                 el.un('mouseleave' ,this.onEventLeave, this);
19674                 el.remove();
19675             },this);
19676         },this);
19677         
19678         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19679             e.remove();
19680         });
19681         
19682     },
19683     
19684     renderEvents: function()
19685     {   
19686         var _this = this;
19687         
19688         this.cells.each(function(c) {
19689             
19690             if(c.row < 5){
19691                 return;
19692             }
19693             
19694             var ev = c.events;
19695             
19696             var r = 4;
19697             if(c.row != c.events.length){
19698                 r = 4 - (4 - (c.row - c.events.length));
19699             }
19700             
19701             c.events = ev.slice(0, r);
19702             c.more = ev.slice(r);
19703             
19704             if(c.more.length && c.more.length == 1){
19705                 c.events.push(c.more.pop());
19706             }
19707             
19708             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19709             
19710         });
19711             
19712         this.cells.each(function(c) {
19713             
19714             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19715             
19716             
19717             for (var e = 0; e < c.events.length; e++){
19718                 var ev = c.events[e];
19719                 var rows = ev.rows;
19720                 
19721                 for(var i = 0; i < rows.length; i++) {
19722                 
19723                     // how many rows should it span..
19724
19725                     var  cfg = {
19726                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19727                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19728
19729                         unselectable : "on",
19730                         cn : [
19731                             {
19732                                 cls: 'fc-event-inner',
19733                                 cn : [
19734     //                                {
19735     //                                  tag:'span',
19736     //                                  cls: 'fc-event-time',
19737     //                                  html : cells.length > 1 ? '' : ev.time
19738     //                                },
19739                                     {
19740                                       tag:'span',
19741                                       cls: 'fc-event-title',
19742                                       html : String.format('{0}', ev.title)
19743                                     }
19744
19745
19746                                 ]
19747                             },
19748                             {
19749                                 cls: 'ui-resizable-handle ui-resizable-e',
19750                                 html : '&nbsp;&nbsp;&nbsp'
19751                             }
19752
19753                         ]
19754                     };
19755
19756                     if (i == 0) {
19757                         cfg.cls += ' fc-event-start';
19758                     }
19759                     if ((i+1) == rows.length) {
19760                         cfg.cls += ' fc-event-end';
19761                     }
19762
19763                     var ctr = _this.el.select('.fc-event-container',true).first();
19764                     var cg = ctr.createChild(cfg);
19765
19766                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19767                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19768
19769                     var r = (c.more.length) ? 1 : 0;
19770                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19771                     cg.setWidth(ebox.right - sbox.x -2);
19772
19773                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19774                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19775                     cg.on('click', _this.onEventClick, _this, ev);
19776
19777                     ev.els.push(cg);
19778                     
19779                 }
19780                 
19781             }
19782             
19783             
19784             if(c.more.length){
19785                 var  cfg = {
19786                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19787                     style : 'position: absolute',
19788                     unselectable : "on",
19789                     cn : [
19790                         {
19791                             cls: 'fc-event-inner',
19792                             cn : [
19793                                 {
19794                                   tag:'span',
19795                                   cls: 'fc-event-title',
19796                                   html : 'More'
19797                                 }
19798
19799
19800                             ]
19801                         },
19802                         {
19803                             cls: 'ui-resizable-handle ui-resizable-e',
19804                             html : '&nbsp;&nbsp;&nbsp'
19805                         }
19806
19807                     ]
19808                 };
19809
19810                 var ctr = _this.el.select('.fc-event-container',true).first();
19811                 var cg = ctr.createChild(cfg);
19812
19813                 var sbox = c.select('.fc-day-content',true).first().getBox();
19814                 var ebox = c.select('.fc-day-content',true).first().getBox();
19815                 //Roo.log(cg);
19816                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19817                 cg.setWidth(ebox.right - sbox.x -2);
19818
19819                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19820                 
19821             }
19822             
19823         });
19824         
19825         
19826         
19827     },
19828     
19829     onEventEnter: function (e, el,event,d) {
19830         this.fireEvent('evententer', this, el, event);
19831     },
19832     
19833     onEventLeave: function (e, el,event,d) {
19834         this.fireEvent('eventleave', this, el, event);
19835     },
19836     
19837     onEventClick: function (e, el,event,d) {
19838         this.fireEvent('eventclick', this, el, event);
19839     },
19840     
19841     onMonthChange: function () {
19842         this.store.load();
19843     },
19844     
19845     onMoreEventClick: function(e, el, more)
19846     {
19847         var _this = this;
19848         
19849         this.calpopover.placement = 'right';
19850         this.calpopover.setTitle('More');
19851         
19852         this.calpopover.setContent('');
19853         
19854         var ctr = this.calpopover.el.select('.popover-content', true).first();
19855         
19856         Roo.each(more, function(m){
19857             var cfg = {
19858                 cls : 'fc-event-hori fc-event-draggable',
19859                 html : m.title
19860             };
19861             var cg = ctr.createChild(cfg);
19862             
19863             cg.on('click', _this.onEventClick, _this, m);
19864         });
19865         
19866         this.calpopover.show(el);
19867         
19868         
19869     },
19870     
19871     onLoad: function () 
19872     {   
19873         this.calevents = [];
19874         var cal = this;
19875         
19876         if(this.store.getCount() > 0){
19877             this.store.data.each(function(d){
19878                cal.addItem({
19879                     id : d.data.id,
19880                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19881                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19882                     time : d.data.start_time,
19883                     title : d.data.title,
19884                     description : d.data.description,
19885                     venue : d.data.venue
19886                 });
19887             });
19888         }
19889         
19890         this.renderEvents();
19891         
19892         if(this.calevents.length && this.loadMask){
19893             this.maskEl.hide();
19894         }
19895     },
19896     
19897     onBeforeLoad: function()
19898     {
19899         this.clearEvents();
19900         if(this.loadMask){
19901             this.maskEl.show();
19902         }
19903     }
19904 });
19905
19906  
19907  /*
19908  * - LGPL
19909  *
19910  * element
19911  * 
19912  */
19913
19914 /**
19915  * @class Roo.bootstrap.Popover
19916  * @extends Roo.bootstrap.Component
19917  * Bootstrap Popover class
19918  * @cfg {String} html contents of the popover   (or false to use children..)
19919  * @cfg {String} title of popover (or false to hide)
19920  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19921  * @cfg {String} trigger click || hover (or false to trigger manually)
19922  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19923  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19924  *      - if false and it has a 'parent' then it will be automatically added to that element
19925  *      - if string - Roo.get  will be called 
19926  * @cfg {Number} delay - delay before showing
19927  
19928  * @constructor
19929  * Create a new Popover
19930  * @param {Object} config The config object
19931  */
19932
19933 Roo.bootstrap.Popover = function(config){
19934     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19935     
19936     this.addEvents({
19937         // raw events
19938          /**
19939          * @event show
19940          * After the popover show
19941          * 
19942          * @param {Roo.bootstrap.Popover} this
19943          */
19944         "show" : true,
19945         /**
19946          * @event hide
19947          * After the popover hide
19948          * 
19949          * @param {Roo.bootstrap.Popover} this
19950          */
19951         "hide" : true
19952     });
19953 };
19954
19955 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19956     
19957     title: false,
19958     html: false,
19959     
19960     placement : 'right',
19961     trigger : 'hover', // hover
19962     modal : false,
19963     delay : 0,
19964     
19965     over: false,
19966     
19967     can_build_overlaid : false,
19968     
19969     maskEl : false, // the mask element
19970     headerEl : false,
19971     contentEl : false,
19972     alignEl : false, // when show is called with an element - this get's stored.
19973     
19974     getChildContainer : function()
19975     {
19976         return this.contentEl;
19977         
19978     },
19979     getPopoverHeader : function()
19980     {
19981         this.title = true; // flag not to hide it..
19982         this.headerEl.addClass('p-0');
19983         return this.headerEl
19984     },
19985     
19986     
19987     getAutoCreate : function(){
19988          
19989         var cfg = {
19990            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19991            style: 'display:block',
19992            cn : [
19993                 {
19994                     cls : 'arrow'
19995                 },
19996                 {
19997                     cls : 'popover-inner ',
19998                     cn : [
19999                         {
20000                             tag: 'h3',
20001                             cls: 'popover-title popover-header',
20002                             html : this.title === false ? '' : this.title
20003                         },
20004                         {
20005                             cls : 'popover-content popover-body '  + (this.cls || ''),
20006                             html : this.html || ''
20007                         }
20008                     ]
20009                     
20010                 }
20011            ]
20012         };
20013         
20014         return cfg;
20015     },
20016     /**
20017      * @param {string} the title
20018      */
20019     setTitle: function(str)
20020     {
20021         this.title = str;
20022         if (this.el) {
20023             this.headerEl.dom.innerHTML = str;
20024         }
20025         
20026     },
20027     /**
20028      * @param {string} the body content
20029      */
20030     setContent: function(str)
20031     {
20032         this.html = str;
20033         if (this.contentEl) {
20034             this.contentEl.dom.innerHTML = str;
20035         }
20036         
20037     },
20038     // as it get's added to the bottom of the page.
20039     onRender : function(ct, position)
20040     {
20041         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20042         
20043         
20044         
20045         if(!this.el){
20046             var cfg = Roo.apply({},  this.getAutoCreate());
20047             cfg.id = Roo.id();
20048             
20049             if (this.cls) {
20050                 cfg.cls += ' ' + this.cls;
20051             }
20052             if (this.style) {
20053                 cfg.style = this.style;
20054             }
20055             //Roo.log("adding to ");
20056             this.el = Roo.get(document.body).createChild(cfg, position);
20057 //            Roo.log(this.el);
20058         }
20059         
20060         this.contentEl = this.el.select('.popover-content',true).first();
20061         this.headerEl =  this.el.select('.popover-title',true).first();
20062         
20063         var nitems = [];
20064         if(typeof(this.items) != 'undefined'){
20065             var items = this.items;
20066             delete this.items;
20067
20068             for(var i =0;i < items.length;i++) {
20069                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20070             }
20071         }
20072
20073         this.items = nitems;
20074         
20075         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20076         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20077         
20078         
20079         
20080         this.initEvents();
20081     },
20082     
20083     resizeMask : function()
20084     {
20085         this.maskEl.setSize(
20086             Roo.lib.Dom.getViewWidth(true),
20087             Roo.lib.Dom.getViewHeight(true)
20088         );
20089     },
20090     
20091     initEvents : function()
20092     {
20093         
20094         if (!this.modal) { 
20095             Roo.bootstrap.Popover.register(this);
20096         }
20097          
20098         this.arrowEl = this.el.select('.arrow',true).first();
20099         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20100         this.el.enableDisplayMode('block');
20101         this.el.hide();
20102  
20103         
20104         if (this.over === false && !this.parent()) {
20105             return; 
20106         }
20107         if (this.triggers === false) {
20108             return;
20109         }
20110          
20111         // support parent
20112         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20113         var triggers = this.trigger ? this.trigger.split(' ') : [];
20114         Roo.each(triggers, function(trigger) {
20115         
20116             if (trigger == 'click') {
20117                 on_el.on('click', this.toggle, this);
20118             } else if (trigger != 'manual') {
20119                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20120                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20121       
20122                 on_el.on(eventIn  ,this.enter, this);
20123                 on_el.on(eventOut, this.leave, this);
20124             }
20125         }, this);
20126     },
20127     
20128     
20129     // private
20130     timeout : null,
20131     hoverState : null,
20132     
20133     toggle : function () {
20134         this.hoverState == 'in' ? this.leave() : this.enter();
20135     },
20136     
20137     enter : function () {
20138         
20139         clearTimeout(this.timeout);
20140     
20141         this.hoverState = 'in';
20142     
20143         if (!this.delay || !this.delay.show) {
20144             this.show();
20145             return;
20146         }
20147         var _t = this;
20148         this.timeout = setTimeout(function () {
20149             if (_t.hoverState == 'in') {
20150                 _t.show();
20151             }
20152         }, this.delay.show)
20153     },
20154     
20155     leave : function() {
20156         clearTimeout(this.timeout);
20157     
20158         this.hoverState = 'out';
20159     
20160         if (!this.delay || !this.delay.hide) {
20161             this.hide();
20162             return;
20163         }
20164         var _t = this;
20165         this.timeout = setTimeout(function () {
20166             if (_t.hoverState == 'out') {
20167                 _t.hide();
20168             }
20169         }, this.delay.hide)
20170     },
20171     /**
20172      * Show the popover
20173      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20174      * @param {string} (left|right|top|bottom) position
20175      */
20176     show : function (on_el, placement)
20177     {
20178         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20179         on_el = on_el || false; // default to false
20180          
20181         if (!on_el) {
20182             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20183                 on_el = this.parent().el;
20184             } else if (this.over) {
20185                 Roo.get(this.over);
20186             }
20187             
20188         }
20189         
20190         this.alignEl = Roo.get( on_el );
20191
20192         if (!this.el) {
20193             this.render(document.body);
20194         }
20195         
20196         
20197          
20198         
20199         if (this.title === false) {
20200             this.headerEl.hide();
20201         }
20202         
20203        
20204         this.el.show();
20205         this.el.dom.style.display = 'block';
20206          
20207  
20208         if (this.alignEl) {
20209             this.updatePosition(this.placement, true);
20210              
20211         } else {
20212             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20213             var es = this.el.getSize();
20214             var x = Roo.lib.Dom.getViewWidth()/2;
20215             var y = Roo.lib.Dom.getViewHeight()/2;
20216             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20217             
20218         }
20219
20220         
20221         //var arrow = this.el.select('.arrow',true).first();
20222         //arrow.set(align[2], 
20223         
20224         this.el.addClass('in');
20225         
20226          
20227         
20228         this.hoverState = 'in';
20229         
20230         if (this.modal) {
20231             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20232             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20233             this.maskEl.dom.style.display = 'block';
20234             this.maskEl.addClass('show');
20235         }
20236         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20237  
20238         this.fireEvent('show', this);
20239         
20240     },
20241     /**
20242      * fire this manually after loading a grid in the table for example
20243      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20244      * @param {Boolean} try and move it if we cant get right position.
20245      */
20246     updatePosition : function(placement, try_move)
20247     {
20248         // allow for calling with no parameters
20249         placement = placement   ? placement :  this.placement;
20250         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20251         
20252         this.el.removeClass([
20253             'fade','top','bottom', 'left', 'right','in',
20254             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20255         ]);
20256         this.el.addClass(placement + ' bs-popover-' + placement);
20257         
20258         if (!this.alignEl ) {
20259             return false;
20260         }
20261         
20262         switch (placement) {
20263             case 'right':
20264                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20265                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20266                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20267                     //normal display... or moved up/down.
20268                     this.el.setXY(offset);
20269                     var xy = this.alignEl.getAnchorXY('tr', false);
20270                     xy[0]+=2;xy[1]+=5;
20271                     this.arrowEl.setXY(xy);
20272                     return true;
20273                 }
20274                 // continue through...
20275                 return this.updatePosition('left', false);
20276                 
20277             
20278             case 'left':
20279                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20280                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20281                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20282                     //normal display... or moved up/down.
20283                     this.el.setXY(offset);
20284                     var xy = this.alignEl.getAnchorXY('tl', false);
20285                     xy[0]-=10;xy[1]+=5; // << fix me
20286                     this.arrowEl.setXY(xy);
20287                     return true;
20288                 }
20289                 // call self...
20290                 return this.updatePosition('right', false);
20291             
20292             case 'top':
20293                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20294                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20295                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20296                     //normal display... or moved up/down.
20297                     this.el.setXY(offset);
20298                     var xy = this.alignEl.getAnchorXY('t', false);
20299                     xy[1]-=10; // << fix me
20300                     this.arrowEl.setXY(xy);
20301                     return true;
20302                 }
20303                 // fall through
20304                return this.updatePosition('bottom', false);
20305             
20306             case 'bottom':
20307                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20308                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20309                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20310                     //normal display... or moved up/down.
20311                     this.el.setXY(offset);
20312                     var xy = this.alignEl.getAnchorXY('b', false);
20313                      xy[1]+=2; // << fix me
20314                     this.arrowEl.setXY(xy);
20315                     return true;
20316                 }
20317                 // fall through
20318                 return this.updatePosition('top', false);
20319                 
20320             
20321         }
20322         
20323         
20324         return false;
20325     },
20326     
20327     hide : function()
20328     {
20329         this.el.setXY([0,0]);
20330         this.el.removeClass('in');
20331         this.el.hide();
20332         this.hoverState = null;
20333         this.maskEl.hide(); // always..
20334         this.fireEvent('hide', this);
20335     }
20336     
20337 });
20338
20339
20340 Roo.apply(Roo.bootstrap.Popover, {
20341
20342     alignment : {
20343         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20344         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20345         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20346         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20347     },
20348     
20349     zIndex : 20001,
20350
20351     clickHander : false,
20352     
20353
20354     onMouseDown : function(e)
20355     {
20356         if (!e.getTarget(".roo-popover")) {
20357             this.hideAll();
20358         }
20359          
20360     },
20361     
20362     popups : [],
20363     
20364     register : function(popup)
20365     {
20366         if (!Roo.bootstrap.Popover.clickHandler) {
20367             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20368         }
20369         // hide other popups.
20370         this.hideAll();
20371         this.popups.push(popup);
20372     },
20373     hideAll : function()
20374     {
20375         this.popups.forEach(function(p) {
20376             p.hide();
20377         });
20378     }
20379
20380 });/*
20381  * - LGPL
20382  *
20383  * Card header - holder for the card header elements.
20384  * 
20385  */
20386
20387 /**
20388  * @class Roo.bootstrap.PopoverNav
20389  * @extends Roo.bootstrap.NavGroup
20390  * Bootstrap Popover header navigation class
20391  * @constructor
20392  * Create a new Popover Header Navigation 
20393  * @param {Object} config The config object
20394  */
20395
20396 Roo.bootstrap.PopoverNav = function(config){
20397     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20398 };
20399
20400 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20401     
20402     
20403     container_method : 'getPopoverHeader' 
20404     
20405      
20406     
20407     
20408    
20409 });
20410
20411  
20412
20413  /*
20414  * - LGPL
20415  *
20416  * Progress
20417  * 
20418  */
20419
20420 /**
20421  * @class Roo.bootstrap.Progress
20422  * @extends Roo.bootstrap.Component
20423  * Bootstrap Progress class
20424  * @cfg {Boolean} striped striped of the progress bar
20425  * @cfg {Boolean} active animated of the progress bar
20426  * 
20427  * 
20428  * @constructor
20429  * Create a new Progress
20430  * @param {Object} config The config object
20431  */
20432
20433 Roo.bootstrap.Progress = function(config){
20434     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20435 };
20436
20437 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20438     
20439     striped : false,
20440     active: false,
20441     
20442     getAutoCreate : function(){
20443         var cfg = {
20444             tag: 'div',
20445             cls: 'progress'
20446         };
20447         
20448         
20449         if(this.striped){
20450             cfg.cls += ' progress-striped';
20451         }
20452       
20453         if(this.active){
20454             cfg.cls += ' active';
20455         }
20456         
20457         
20458         return cfg;
20459     }
20460    
20461 });
20462
20463  
20464
20465  /*
20466  * - LGPL
20467  *
20468  * ProgressBar
20469  * 
20470  */
20471
20472 /**
20473  * @class Roo.bootstrap.ProgressBar
20474  * @extends Roo.bootstrap.Component
20475  * Bootstrap ProgressBar class
20476  * @cfg {Number} aria_valuenow aria-value now
20477  * @cfg {Number} aria_valuemin aria-value min
20478  * @cfg {Number} aria_valuemax aria-value max
20479  * @cfg {String} label label for the progress bar
20480  * @cfg {String} panel (success | info | warning | danger )
20481  * @cfg {String} role role of the progress bar
20482  * @cfg {String} sr_only text
20483  * 
20484  * 
20485  * @constructor
20486  * Create a new ProgressBar
20487  * @param {Object} config The config object
20488  */
20489
20490 Roo.bootstrap.ProgressBar = function(config){
20491     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20492 };
20493
20494 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20495     
20496     aria_valuenow : 0,
20497     aria_valuemin : 0,
20498     aria_valuemax : 100,
20499     label : false,
20500     panel : false,
20501     role : false,
20502     sr_only: false,
20503     
20504     getAutoCreate : function()
20505     {
20506         
20507         var cfg = {
20508             tag: 'div',
20509             cls: 'progress-bar',
20510             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20511         };
20512         
20513         if(this.sr_only){
20514             cfg.cn = {
20515                 tag: 'span',
20516                 cls: 'sr-only',
20517                 html: this.sr_only
20518             }
20519         }
20520         
20521         if(this.role){
20522             cfg.role = this.role;
20523         }
20524         
20525         if(this.aria_valuenow){
20526             cfg['aria-valuenow'] = this.aria_valuenow;
20527         }
20528         
20529         if(this.aria_valuemin){
20530             cfg['aria-valuemin'] = this.aria_valuemin;
20531         }
20532         
20533         if(this.aria_valuemax){
20534             cfg['aria-valuemax'] = this.aria_valuemax;
20535         }
20536         
20537         if(this.label && !this.sr_only){
20538             cfg.html = this.label;
20539         }
20540         
20541         if(this.panel){
20542             cfg.cls += ' progress-bar-' + this.panel;
20543         }
20544         
20545         return cfg;
20546     },
20547     
20548     update : function(aria_valuenow)
20549     {
20550         this.aria_valuenow = aria_valuenow;
20551         
20552         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20553     }
20554    
20555 });
20556
20557  
20558
20559  /*
20560  * - LGPL
20561  *
20562  * column
20563  * 
20564  */
20565
20566 /**
20567  * @class Roo.bootstrap.TabGroup
20568  * @extends Roo.bootstrap.Column
20569  * Bootstrap Column class
20570  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20571  * @cfg {Boolean} carousel true to make the group behave like a carousel
20572  * @cfg {Boolean} bullets show bullets for the panels
20573  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20574  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20575  * @cfg {Boolean} showarrow (true|false) show arrow default true
20576  * 
20577  * @constructor
20578  * Create a new TabGroup
20579  * @param {Object} config The config object
20580  */
20581
20582 Roo.bootstrap.TabGroup = function(config){
20583     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20584     if (!this.navId) {
20585         this.navId = Roo.id();
20586     }
20587     this.tabs = [];
20588     Roo.bootstrap.TabGroup.register(this);
20589     
20590 };
20591
20592 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20593     
20594     carousel : false,
20595     transition : false,
20596     bullets : 0,
20597     timer : 0,
20598     autoslide : false,
20599     slideFn : false,
20600     slideOnTouch : false,
20601     showarrow : true,
20602     
20603     getAutoCreate : function()
20604     {
20605         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20606         
20607         cfg.cls += ' tab-content';
20608         
20609         if (this.carousel) {
20610             cfg.cls += ' carousel slide';
20611             
20612             cfg.cn = [{
20613                cls : 'carousel-inner',
20614                cn : []
20615             }];
20616         
20617             if(this.bullets  && !Roo.isTouch){
20618                 
20619                 var bullets = {
20620                     cls : 'carousel-bullets',
20621                     cn : []
20622                 };
20623                
20624                 if(this.bullets_cls){
20625                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20626                 }
20627                 
20628                 bullets.cn.push({
20629                     cls : 'clear'
20630                 });
20631                 
20632                 cfg.cn[0].cn.push(bullets);
20633             }
20634             
20635             if(this.showarrow){
20636                 cfg.cn[0].cn.push({
20637                     tag : 'div',
20638                     class : 'carousel-arrow',
20639                     cn : [
20640                         {
20641                             tag : 'div',
20642                             class : 'carousel-prev',
20643                             cn : [
20644                                 {
20645                                     tag : 'i',
20646                                     class : 'fa fa-chevron-left'
20647                                 }
20648                             ]
20649                         },
20650                         {
20651                             tag : 'div',
20652                             class : 'carousel-next',
20653                             cn : [
20654                                 {
20655                                     tag : 'i',
20656                                     class : 'fa fa-chevron-right'
20657                                 }
20658                             ]
20659                         }
20660                     ]
20661                 });
20662             }
20663             
20664         }
20665         
20666         return cfg;
20667     },
20668     
20669     initEvents:  function()
20670     {
20671 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20672 //            this.el.on("touchstart", this.onTouchStart, this);
20673 //        }
20674         
20675         if(this.autoslide){
20676             var _this = this;
20677             
20678             this.slideFn = window.setInterval(function() {
20679                 _this.showPanelNext();
20680             }, this.timer);
20681         }
20682         
20683         if(this.showarrow){
20684             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20685             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20686         }
20687         
20688         
20689     },
20690     
20691 //    onTouchStart : function(e, el, o)
20692 //    {
20693 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20694 //            return;
20695 //        }
20696 //        
20697 //        this.showPanelNext();
20698 //    },
20699     
20700     
20701     getChildContainer : function()
20702     {
20703         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20704     },
20705     
20706     /**
20707     * register a Navigation item
20708     * @param {Roo.bootstrap.NavItem} the navitem to add
20709     */
20710     register : function(item)
20711     {
20712         this.tabs.push( item);
20713         item.navId = this.navId; // not really needed..
20714         this.addBullet();
20715     
20716     },
20717     
20718     getActivePanel : function()
20719     {
20720         var r = false;
20721         Roo.each(this.tabs, function(t) {
20722             if (t.active) {
20723                 r = t;
20724                 return false;
20725             }
20726             return null;
20727         });
20728         return r;
20729         
20730     },
20731     getPanelByName : function(n)
20732     {
20733         var r = false;
20734         Roo.each(this.tabs, function(t) {
20735             if (t.tabId == n) {
20736                 r = t;
20737                 return false;
20738             }
20739             return null;
20740         });
20741         return r;
20742     },
20743     indexOfPanel : function(p)
20744     {
20745         var r = false;
20746         Roo.each(this.tabs, function(t,i) {
20747             if (t.tabId == p.tabId) {
20748                 r = i;
20749                 return false;
20750             }
20751             return null;
20752         });
20753         return r;
20754     },
20755     /**
20756      * show a specific panel
20757      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20758      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20759      */
20760     showPanel : function (pan)
20761     {
20762         if(this.transition || typeof(pan) == 'undefined'){
20763             Roo.log("waiting for the transitionend");
20764             return false;
20765         }
20766         
20767         if (typeof(pan) == 'number') {
20768             pan = this.tabs[pan];
20769         }
20770         
20771         if (typeof(pan) == 'string') {
20772             pan = this.getPanelByName(pan);
20773         }
20774         
20775         var cur = this.getActivePanel();
20776         
20777         if(!pan || !cur){
20778             Roo.log('pan or acitve pan is undefined');
20779             return false;
20780         }
20781         
20782         if (pan.tabId == this.getActivePanel().tabId) {
20783             return true;
20784         }
20785         
20786         if (false === cur.fireEvent('beforedeactivate')) {
20787             return false;
20788         }
20789         
20790         if(this.bullets > 0 && !Roo.isTouch){
20791             this.setActiveBullet(this.indexOfPanel(pan));
20792         }
20793         
20794         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20795             
20796             //class="carousel-item carousel-item-next carousel-item-left"
20797             
20798             this.transition = true;
20799             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20800             var lr = dir == 'next' ? 'left' : 'right';
20801             pan.el.addClass(dir); // or prev
20802             pan.el.addClass('carousel-item-' + dir); // or prev
20803             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20804             cur.el.addClass(lr); // or right
20805             pan.el.addClass(lr);
20806             cur.el.addClass('carousel-item-' +lr); // or right
20807             pan.el.addClass('carousel-item-' +lr);
20808             
20809             
20810             var _this = this;
20811             cur.el.on('transitionend', function() {
20812                 Roo.log("trans end?");
20813                 
20814                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20815                 pan.setActive(true);
20816                 
20817                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20818                 cur.setActive(false);
20819                 
20820                 _this.transition = false;
20821                 
20822             }, this, { single:  true } );
20823             
20824             return true;
20825         }
20826         
20827         cur.setActive(false);
20828         pan.setActive(true);
20829         
20830         return true;
20831         
20832     },
20833     showPanelNext : function()
20834     {
20835         var i = this.indexOfPanel(this.getActivePanel());
20836         
20837         if (i >= this.tabs.length - 1 && !this.autoslide) {
20838             return;
20839         }
20840         
20841         if (i >= this.tabs.length - 1 && this.autoslide) {
20842             i = -1;
20843         }
20844         
20845         this.showPanel(this.tabs[i+1]);
20846     },
20847     
20848     showPanelPrev : function()
20849     {
20850         var i = this.indexOfPanel(this.getActivePanel());
20851         
20852         if (i  < 1 && !this.autoslide) {
20853             return;
20854         }
20855         
20856         if (i < 1 && this.autoslide) {
20857             i = this.tabs.length;
20858         }
20859         
20860         this.showPanel(this.tabs[i-1]);
20861     },
20862     
20863     
20864     addBullet: function()
20865     {
20866         if(!this.bullets || Roo.isTouch){
20867             return;
20868         }
20869         var ctr = this.el.select('.carousel-bullets',true).first();
20870         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20871         var bullet = ctr.createChild({
20872             cls : 'bullet bullet-' + i
20873         },ctr.dom.lastChild);
20874         
20875         
20876         var _this = this;
20877         
20878         bullet.on('click', (function(e, el, o, ii, t){
20879
20880             e.preventDefault();
20881
20882             this.showPanel(ii);
20883
20884             if(this.autoslide && this.slideFn){
20885                 clearInterval(this.slideFn);
20886                 this.slideFn = window.setInterval(function() {
20887                     _this.showPanelNext();
20888                 }, this.timer);
20889             }
20890
20891         }).createDelegate(this, [i, bullet], true));
20892                 
20893         
20894     },
20895      
20896     setActiveBullet : function(i)
20897     {
20898         if(Roo.isTouch){
20899             return;
20900         }
20901         
20902         Roo.each(this.el.select('.bullet', true).elements, function(el){
20903             el.removeClass('selected');
20904         });
20905
20906         var bullet = this.el.select('.bullet-' + i, true).first();
20907         
20908         if(!bullet){
20909             return;
20910         }
20911         
20912         bullet.addClass('selected');
20913     }
20914     
20915     
20916   
20917 });
20918
20919  
20920
20921  
20922  
20923 Roo.apply(Roo.bootstrap.TabGroup, {
20924     
20925     groups: {},
20926      /**
20927     * register a Navigation Group
20928     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20929     */
20930     register : function(navgrp)
20931     {
20932         this.groups[navgrp.navId] = navgrp;
20933         
20934     },
20935     /**
20936     * fetch a Navigation Group based on the navigation ID
20937     * if one does not exist , it will get created.
20938     * @param {string} the navgroup to add
20939     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20940     */
20941     get: function(navId) {
20942         if (typeof(this.groups[navId]) == 'undefined') {
20943             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20944         }
20945         return this.groups[navId] ;
20946     }
20947     
20948     
20949     
20950 });
20951
20952  /*
20953  * - LGPL
20954  *
20955  * TabPanel
20956  * 
20957  */
20958
20959 /**
20960  * @class Roo.bootstrap.TabPanel
20961  * @extends Roo.bootstrap.Component
20962  * Bootstrap TabPanel class
20963  * @cfg {Boolean} active panel active
20964  * @cfg {String} html panel content
20965  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20966  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20967  * @cfg {String} href click to link..
20968  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20969  * 
20970  * 
20971  * @constructor
20972  * Create a new TabPanel
20973  * @param {Object} config The config object
20974  */
20975
20976 Roo.bootstrap.TabPanel = function(config){
20977     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20978     this.addEvents({
20979         /**
20980              * @event changed
20981              * Fires when the active status changes
20982              * @param {Roo.bootstrap.TabPanel} this
20983              * @param {Boolean} state the new state
20984             
20985          */
20986         'changed': true,
20987         /**
20988              * @event beforedeactivate
20989              * Fires before a tab is de-activated - can be used to do validation on a form.
20990              * @param {Roo.bootstrap.TabPanel} this
20991              * @return {Boolean} false if there is an error
20992             
20993          */
20994         'beforedeactivate': true
20995      });
20996     
20997     this.tabId = this.tabId || Roo.id();
20998   
20999 };
21000
21001 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21002     
21003     active: false,
21004     html: false,
21005     tabId: false,
21006     navId : false,
21007     href : '',
21008     touchSlide : false,
21009     getAutoCreate : function(){
21010         
21011         
21012         var cfg = {
21013             tag: 'div',
21014             // item is needed for carousel - not sure if it has any effect otherwise
21015             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21016             html: this.html || ''
21017         };
21018         
21019         if(this.active){
21020             cfg.cls += ' active';
21021         }
21022         
21023         if(this.tabId){
21024             cfg.tabId = this.tabId;
21025         }
21026         
21027         
21028         
21029         return cfg;
21030     },
21031     
21032     initEvents:  function()
21033     {
21034         var p = this.parent();
21035         
21036         this.navId = this.navId || p.navId;
21037         
21038         if (typeof(this.navId) != 'undefined') {
21039             // not really needed.. but just in case.. parent should be a NavGroup.
21040             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21041             
21042             tg.register(this);
21043             
21044             var i = tg.tabs.length - 1;
21045             
21046             if(this.active && tg.bullets > 0 && i < tg.bullets){
21047                 tg.setActiveBullet(i);
21048             }
21049         }
21050         
21051         this.el.on('click', this.onClick, this);
21052         
21053         if(Roo.isTouch && this.touchSlide){
21054             this.el.on("touchstart", this.onTouchStart, this);
21055             this.el.on("touchmove", this.onTouchMove, this);
21056             this.el.on("touchend", this.onTouchEnd, this);
21057         }
21058         
21059     },
21060     
21061     onRender : function(ct, position)
21062     {
21063         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21064     },
21065     
21066     setActive : function(state)
21067     {
21068         Roo.log("panel - set active " + this.tabId + "=" + state);
21069         
21070         this.active = state;
21071         if (!state) {
21072             this.el.removeClass('active');
21073             
21074         } else  if (!this.el.hasClass('active')) {
21075             this.el.addClass('active');
21076         }
21077         
21078         this.fireEvent('changed', this, state);
21079     },
21080     
21081     onClick : function(e)
21082     {
21083         e.preventDefault();
21084         
21085         if(!this.href.length){
21086             return;
21087         }
21088         
21089         window.location.href = this.href;
21090     },
21091     
21092     startX : 0,
21093     startY : 0,
21094     endX : 0,
21095     endY : 0,
21096     swiping : false,
21097     
21098     onTouchStart : function(e)
21099     {
21100         this.swiping = false;
21101         
21102         this.startX = e.browserEvent.touches[0].clientX;
21103         this.startY = e.browserEvent.touches[0].clientY;
21104     },
21105     
21106     onTouchMove : function(e)
21107     {
21108         this.swiping = true;
21109         
21110         this.endX = e.browserEvent.touches[0].clientX;
21111         this.endY = e.browserEvent.touches[0].clientY;
21112     },
21113     
21114     onTouchEnd : function(e)
21115     {
21116         if(!this.swiping){
21117             this.onClick(e);
21118             return;
21119         }
21120         
21121         var tabGroup = this.parent();
21122         
21123         if(this.endX > this.startX){ // swiping right
21124             tabGroup.showPanelPrev();
21125             return;
21126         }
21127         
21128         if(this.startX > this.endX){ // swiping left
21129             tabGroup.showPanelNext();
21130             return;
21131         }
21132     }
21133     
21134     
21135 });
21136  
21137
21138  
21139
21140  /*
21141  * - LGPL
21142  *
21143  * DateField
21144  * 
21145  */
21146
21147 /**
21148  * @class Roo.bootstrap.DateField
21149  * @extends Roo.bootstrap.Input
21150  * Bootstrap DateField class
21151  * @cfg {Number} weekStart default 0
21152  * @cfg {String} viewMode default empty, (months|years)
21153  * @cfg {String} minViewMode default empty, (months|years)
21154  * @cfg {Number} startDate default -Infinity
21155  * @cfg {Number} endDate default Infinity
21156  * @cfg {Boolean} todayHighlight default false
21157  * @cfg {Boolean} todayBtn default false
21158  * @cfg {Boolean} calendarWeeks default false
21159  * @cfg {Object} daysOfWeekDisabled default empty
21160  * @cfg {Boolean} singleMode default false (true | false)
21161  * 
21162  * @cfg {Boolean} keyboardNavigation default true
21163  * @cfg {String} language default en
21164  * 
21165  * @constructor
21166  * Create a new DateField
21167  * @param {Object} config The config object
21168  */
21169
21170 Roo.bootstrap.DateField = function(config){
21171     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21172      this.addEvents({
21173             /**
21174              * @event show
21175              * Fires when this field show.
21176              * @param {Roo.bootstrap.DateField} this
21177              * @param {Mixed} date The date value
21178              */
21179             show : true,
21180             /**
21181              * @event show
21182              * Fires when this field hide.
21183              * @param {Roo.bootstrap.DateField} this
21184              * @param {Mixed} date The date value
21185              */
21186             hide : true,
21187             /**
21188              * @event select
21189              * Fires when select a date.
21190              * @param {Roo.bootstrap.DateField} this
21191              * @param {Mixed} date The date value
21192              */
21193             select : true,
21194             /**
21195              * @event beforeselect
21196              * Fires when before select a date.
21197              * @param {Roo.bootstrap.DateField} this
21198              * @param {Mixed} date The date value
21199              */
21200             beforeselect : true
21201         });
21202 };
21203
21204 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21205     
21206     /**
21207      * @cfg {String} format
21208      * The default date format string which can be overriden for localization support.  The format must be
21209      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21210      */
21211     format : "m/d/y",
21212     /**
21213      * @cfg {String} altFormats
21214      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21215      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21216      */
21217     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21218     
21219     weekStart : 0,
21220     
21221     viewMode : '',
21222     
21223     minViewMode : '',
21224     
21225     todayHighlight : false,
21226     
21227     todayBtn: false,
21228     
21229     language: 'en',
21230     
21231     keyboardNavigation: true,
21232     
21233     calendarWeeks: false,
21234     
21235     startDate: -Infinity,
21236     
21237     endDate: Infinity,
21238     
21239     daysOfWeekDisabled: [],
21240     
21241     _events: [],
21242     
21243     singleMode : false,
21244     
21245     UTCDate: function()
21246     {
21247         return new Date(Date.UTC.apply(Date, arguments));
21248     },
21249     
21250     UTCToday: function()
21251     {
21252         var today = new Date();
21253         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21254     },
21255     
21256     getDate: function() {
21257             var d = this.getUTCDate();
21258             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21259     },
21260     
21261     getUTCDate: function() {
21262             return this.date;
21263     },
21264     
21265     setDate: function(d) {
21266             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21267     },
21268     
21269     setUTCDate: function(d) {
21270             this.date = d;
21271             this.setValue(this.formatDate(this.date));
21272     },
21273         
21274     onRender: function(ct, position)
21275     {
21276         
21277         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21278         
21279         this.language = this.language || 'en';
21280         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21281         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21282         
21283         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21284         this.format = this.format || 'm/d/y';
21285         this.isInline = false;
21286         this.isInput = true;
21287         this.component = this.el.select('.add-on', true).first() || false;
21288         this.component = (this.component && this.component.length === 0) ? false : this.component;
21289         this.hasInput = this.component && this.inputEl().length;
21290         
21291         if (typeof(this.minViewMode === 'string')) {
21292             switch (this.minViewMode) {
21293                 case 'months':
21294                     this.minViewMode = 1;
21295                     break;
21296                 case 'years':
21297                     this.minViewMode = 2;
21298                     break;
21299                 default:
21300                     this.minViewMode = 0;
21301                     break;
21302             }
21303         }
21304         
21305         if (typeof(this.viewMode === 'string')) {
21306             switch (this.viewMode) {
21307                 case 'months':
21308                     this.viewMode = 1;
21309                     break;
21310                 case 'years':
21311                     this.viewMode = 2;
21312                     break;
21313                 default:
21314                     this.viewMode = 0;
21315                     break;
21316             }
21317         }
21318                 
21319         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21320         
21321 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21322         
21323         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21324         
21325         this.picker().on('mousedown', this.onMousedown, this);
21326         this.picker().on('click', this.onClick, this);
21327         
21328         this.picker().addClass('datepicker-dropdown');
21329         
21330         this.startViewMode = this.viewMode;
21331         
21332         if(this.singleMode){
21333             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21334                 v.setVisibilityMode(Roo.Element.DISPLAY);
21335                 v.hide();
21336             });
21337             
21338             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21339                 v.setStyle('width', '189px');
21340             });
21341         }
21342         
21343         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21344             if(!this.calendarWeeks){
21345                 v.remove();
21346                 return;
21347             }
21348             
21349             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21350             v.attr('colspan', function(i, val){
21351                 return parseInt(val) + 1;
21352             });
21353         });
21354                         
21355         
21356         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21357         
21358         this.setStartDate(this.startDate);
21359         this.setEndDate(this.endDate);
21360         
21361         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21362         
21363         this.fillDow();
21364         this.fillMonths();
21365         this.update();
21366         this.showMode();
21367         
21368         if(this.isInline) {
21369             this.showPopup();
21370         }
21371     },
21372     
21373     picker : function()
21374     {
21375         return this.pickerEl;
21376 //        return this.el.select('.datepicker', true).first();
21377     },
21378     
21379     fillDow: function()
21380     {
21381         var dowCnt = this.weekStart;
21382         
21383         var dow = {
21384             tag: 'tr',
21385             cn: [
21386                 
21387             ]
21388         };
21389         
21390         if(this.calendarWeeks){
21391             dow.cn.push({
21392                 tag: 'th',
21393                 cls: 'cw',
21394                 html: '&nbsp;'
21395             })
21396         }
21397         
21398         while (dowCnt < this.weekStart + 7) {
21399             dow.cn.push({
21400                 tag: 'th',
21401                 cls: 'dow',
21402                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21403             });
21404         }
21405         
21406         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21407     },
21408     
21409     fillMonths: function()
21410     {    
21411         var i = 0;
21412         var months = this.picker().select('>.datepicker-months td', true).first();
21413         
21414         months.dom.innerHTML = '';
21415         
21416         while (i < 12) {
21417             var month = {
21418                 tag: 'span',
21419                 cls: 'month',
21420                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21421             };
21422             
21423             months.createChild(month);
21424         }
21425         
21426     },
21427     
21428     update: function()
21429     {
21430         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;
21431         
21432         if (this.date < this.startDate) {
21433             this.viewDate = new Date(this.startDate);
21434         } else if (this.date > this.endDate) {
21435             this.viewDate = new Date(this.endDate);
21436         } else {
21437             this.viewDate = new Date(this.date);
21438         }
21439         
21440         this.fill();
21441     },
21442     
21443     fill: function() 
21444     {
21445         var d = new Date(this.viewDate),
21446                 year = d.getUTCFullYear(),
21447                 month = d.getUTCMonth(),
21448                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21449                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21450                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21451                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21452                 currentDate = this.date && this.date.valueOf(),
21453                 today = this.UTCToday();
21454         
21455         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21456         
21457 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21458         
21459 //        this.picker.select('>tfoot th.today').
21460 //                                              .text(dates[this.language].today)
21461 //                                              .toggle(this.todayBtn !== false);
21462     
21463         this.updateNavArrows();
21464         this.fillMonths();
21465                                                 
21466         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21467         
21468         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21469          
21470         prevMonth.setUTCDate(day);
21471         
21472         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21473         
21474         var nextMonth = new Date(prevMonth);
21475         
21476         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21477         
21478         nextMonth = nextMonth.valueOf();
21479         
21480         var fillMonths = false;
21481         
21482         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21483         
21484         while(prevMonth.valueOf() <= nextMonth) {
21485             var clsName = '';
21486             
21487             if (prevMonth.getUTCDay() === this.weekStart) {
21488                 if(fillMonths){
21489                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21490                 }
21491                     
21492                 fillMonths = {
21493                     tag: 'tr',
21494                     cn: []
21495                 };
21496                 
21497                 if(this.calendarWeeks){
21498                     // ISO 8601: First week contains first thursday.
21499                     // ISO also states week starts on Monday, but we can be more abstract here.
21500                     var
21501                     // Start of current week: based on weekstart/current date
21502                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21503                     // Thursday of this week
21504                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21505                     // First Thursday of year, year from thursday
21506                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21507                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21508                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21509                     
21510                     fillMonths.cn.push({
21511                         tag: 'td',
21512                         cls: 'cw',
21513                         html: calWeek
21514                     });
21515                 }
21516             }
21517             
21518             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21519                 clsName += ' old';
21520             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21521                 clsName += ' new';
21522             }
21523             if (this.todayHighlight &&
21524                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21525                 prevMonth.getUTCMonth() == today.getMonth() &&
21526                 prevMonth.getUTCDate() == today.getDate()) {
21527                 clsName += ' today';
21528             }
21529             
21530             if (currentDate && prevMonth.valueOf() === currentDate) {
21531                 clsName += ' active';
21532             }
21533             
21534             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21535                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21536                     clsName += ' disabled';
21537             }
21538             
21539             fillMonths.cn.push({
21540                 tag: 'td',
21541                 cls: 'day ' + clsName,
21542                 html: prevMonth.getDate()
21543             });
21544             
21545             prevMonth.setDate(prevMonth.getDate()+1);
21546         }
21547           
21548         var currentYear = this.date && this.date.getUTCFullYear();
21549         var currentMonth = this.date && this.date.getUTCMonth();
21550         
21551         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21552         
21553         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21554             v.removeClass('active');
21555             
21556             if(currentYear === year && k === currentMonth){
21557                 v.addClass('active');
21558             }
21559             
21560             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21561                 v.addClass('disabled');
21562             }
21563             
21564         });
21565         
21566         
21567         year = parseInt(year/10, 10) * 10;
21568         
21569         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21570         
21571         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21572         
21573         year -= 1;
21574         for (var i = -1; i < 11; i++) {
21575             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21576                 tag: 'span',
21577                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21578                 html: year
21579             });
21580             
21581             year += 1;
21582         }
21583     },
21584     
21585     showMode: function(dir) 
21586     {
21587         if (dir) {
21588             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21589         }
21590         
21591         Roo.each(this.picker().select('>div',true).elements, function(v){
21592             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21593             v.hide();
21594         });
21595         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21596     },
21597     
21598     place: function()
21599     {
21600         if(this.isInline) {
21601             return;
21602         }
21603         
21604         this.picker().removeClass(['bottom', 'top']);
21605         
21606         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21607             /*
21608              * place to the top of element!
21609              *
21610              */
21611             
21612             this.picker().addClass('top');
21613             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21614             
21615             return;
21616         }
21617         
21618         this.picker().addClass('bottom');
21619         
21620         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21621     },
21622     
21623     parseDate : function(value)
21624     {
21625         if(!value || value instanceof Date){
21626             return value;
21627         }
21628         var v = Date.parseDate(value, this.format);
21629         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21630             v = Date.parseDate(value, 'Y-m-d');
21631         }
21632         if(!v && this.altFormats){
21633             if(!this.altFormatsArray){
21634                 this.altFormatsArray = this.altFormats.split("|");
21635             }
21636             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21637                 v = Date.parseDate(value, this.altFormatsArray[i]);
21638             }
21639         }
21640         return v;
21641     },
21642     
21643     formatDate : function(date, fmt)
21644     {   
21645         return (!date || !(date instanceof Date)) ?
21646         date : date.dateFormat(fmt || this.format);
21647     },
21648     
21649     onFocus : function()
21650     {
21651         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21652         this.showPopup();
21653     },
21654     
21655     onBlur : function()
21656     {
21657         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21658         
21659         var d = this.inputEl().getValue();
21660         
21661         this.setValue(d);
21662                 
21663         this.hidePopup();
21664     },
21665     
21666     showPopup : function()
21667     {
21668         this.picker().show();
21669         this.update();
21670         this.place();
21671         
21672         this.fireEvent('showpopup', this, this.date);
21673     },
21674     
21675     hidePopup : function()
21676     {
21677         if(this.isInline) {
21678             return;
21679         }
21680         this.picker().hide();
21681         this.viewMode = this.startViewMode;
21682         this.showMode();
21683         
21684         this.fireEvent('hidepopup', this, this.date);
21685         
21686     },
21687     
21688     onMousedown: function(e)
21689     {
21690         e.stopPropagation();
21691         e.preventDefault();
21692     },
21693     
21694     keyup: function(e)
21695     {
21696         Roo.bootstrap.DateField.superclass.keyup.call(this);
21697         this.update();
21698     },
21699
21700     setValue: function(v)
21701     {
21702         if(this.fireEvent('beforeselect', this, v) !== false){
21703             var d = new Date(this.parseDate(v) ).clearTime();
21704         
21705             if(isNaN(d.getTime())){
21706                 this.date = this.viewDate = '';
21707                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21708                 return;
21709             }
21710
21711             v = this.formatDate(d);
21712
21713             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21714
21715             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21716
21717             this.update();
21718
21719             this.fireEvent('select', this, this.date);
21720         }
21721     },
21722     
21723     getValue: function()
21724     {
21725         return this.formatDate(this.date);
21726     },
21727     
21728     fireKey: function(e)
21729     {
21730         if (!this.picker().isVisible()){
21731             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21732                 this.showPopup();
21733             }
21734             return;
21735         }
21736         
21737         var dateChanged = false,
21738         dir, day, month,
21739         newDate, newViewDate;
21740         
21741         switch(e.keyCode){
21742             case 27: // escape
21743                 this.hidePopup();
21744                 e.preventDefault();
21745                 break;
21746             case 37: // left
21747             case 39: // right
21748                 if (!this.keyboardNavigation) {
21749                     break;
21750                 }
21751                 dir = e.keyCode == 37 ? -1 : 1;
21752                 
21753                 if (e.ctrlKey){
21754                     newDate = this.moveYear(this.date, dir);
21755                     newViewDate = this.moveYear(this.viewDate, dir);
21756                 } else if (e.shiftKey){
21757                     newDate = this.moveMonth(this.date, dir);
21758                     newViewDate = this.moveMonth(this.viewDate, dir);
21759                 } else {
21760                     newDate = new Date(this.date);
21761                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21762                     newViewDate = new Date(this.viewDate);
21763                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21764                 }
21765                 if (this.dateWithinRange(newDate)){
21766                     this.date = newDate;
21767                     this.viewDate = newViewDate;
21768                     this.setValue(this.formatDate(this.date));
21769 //                    this.update();
21770                     e.preventDefault();
21771                     dateChanged = true;
21772                 }
21773                 break;
21774             case 38: // up
21775             case 40: // down
21776                 if (!this.keyboardNavigation) {
21777                     break;
21778                 }
21779                 dir = e.keyCode == 38 ? -1 : 1;
21780                 if (e.ctrlKey){
21781                     newDate = this.moveYear(this.date, dir);
21782                     newViewDate = this.moveYear(this.viewDate, dir);
21783                 } else if (e.shiftKey){
21784                     newDate = this.moveMonth(this.date, dir);
21785                     newViewDate = this.moveMonth(this.viewDate, dir);
21786                 } else {
21787                     newDate = new Date(this.date);
21788                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21789                     newViewDate = new Date(this.viewDate);
21790                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21791                 }
21792                 if (this.dateWithinRange(newDate)){
21793                     this.date = newDate;
21794                     this.viewDate = newViewDate;
21795                     this.setValue(this.formatDate(this.date));
21796 //                    this.update();
21797                     e.preventDefault();
21798                     dateChanged = true;
21799                 }
21800                 break;
21801             case 13: // enter
21802                 this.setValue(this.formatDate(this.date));
21803                 this.hidePopup();
21804                 e.preventDefault();
21805                 break;
21806             case 9: // tab
21807                 this.setValue(this.formatDate(this.date));
21808                 this.hidePopup();
21809                 break;
21810             case 16: // shift
21811             case 17: // ctrl
21812             case 18: // alt
21813                 break;
21814             default :
21815                 this.hidePopup();
21816                 
21817         }
21818     },
21819     
21820     
21821     onClick: function(e) 
21822     {
21823         e.stopPropagation();
21824         e.preventDefault();
21825         
21826         var target = e.getTarget();
21827         
21828         if(target.nodeName.toLowerCase() === 'i'){
21829             target = Roo.get(target).dom.parentNode;
21830         }
21831         
21832         var nodeName = target.nodeName;
21833         var className = target.className;
21834         var html = target.innerHTML;
21835         //Roo.log(nodeName);
21836         
21837         switch(nodeName.toLowerCase()) {
21838             case 'th':
21839                 switch(className) {
21840                     case 'switch':
21841                         this.showMode(1);
21842                         break;
21843                     case 'prev':
21844                     case 'next':
21845                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21846                         switch(this.viewMode){
21847                                 case 0:
21848                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21849                                         break;
21850                                 case 1:
21851                                 case 2:
21852                                         this.viewDate = this.moveYear(this.viewDate, dir);
21853                                         break;
21854                         }
21855                         this.fill();
21856                         break;
21857                     case 'today':
21858                         var date = new Date();
21859                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21860 //                        this.fill()
21861                         this.setValue(this.formatDate(this.date));
21862                         
21863                         this.hidePopup();
21864                         break;
21865                 }
21866                 break;
21867             case 'span':
21868                 if (className.indexOf('disabled') < 0) {
21869                     this.viewDate.setUTCDate(1);
21870                     if (className.indexOf('month') > -1) {
21871                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21872                     } else {
21873                         var year = parseInt(html, 10) || 0;
21874                         this.viewDate.setUTCFullYear(year);
21875                         
21876                     }
21877                     
21878                     if(this.singleMode){
21879                         this.setValue(this.formatDate(this.viewDate));
21880                         this.hidePopup();
21881                         return;
21882                     }
21883                     
21884                     this.showMode(-1);
21885                     this.fill();
21886                 }
21887                 break;
21888                 
21889             case 'td':
21890                 //Roo.log(className);
21891                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21892                     var day = parseInt(html, 10) || 1;
21893                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21894                         month = (this.viewDate || new Date()).getUTCMonth();
21895
21896                     if (className.indexOf('old') > -1) {
21897                         if(month === 0 ){
21898                             month = 11;
21899                             year -= 1;
21900                         }else{
21901                             month -= 1;
21902                         }
21903                     } else if (className.indexOf('new') > -1) {
21904                         if (month == 11) {
21905                             month = 0;
21906                             year += 1;
21907                         } else {
21908                             month += 1;
21909                         }
21910                     }
21911                     //Roo.log([year,month,day]);
21912                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21913                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21914 //                    this.fill();
21915                     //Roo.log(this.formatDate(this.date));
21916                     this.setValue(this.formatDate(this.date));
21917                     this.hidePopup();
21918                 }
21919                 break;
21920         }
21921     },
21922     
21923     setStartDate: function(startDate)
21924     {
21925         this.startDate = startDate || -Infinity;
21926         if (this.startDate !== -Infinity) {
21927             this.startDate = this.parseDate(this.startDate);
21928         }
21929         this.update();
21930         this.updateNavArrows();
21931     },
21932
21933     setEndDate: function(endDate)
21934     {
21935         this.endDate = endDate || Infinity;
21936         if (this.endDate !== Infinity) {
21937             this.endDate = this.parseDate(this.endDate);
21938         }
21939         this.update();
21940         this.updateNavArrows();
21941     },
21942     
21943     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21944     {
21945         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21946         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21947             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21948         }
21949         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21950             return parseInt(d, 10);
21951         });
21952         this.update();
21953         this.updateNavArrows();
21954     },
21955     
21956     updateNavArrows: function() 
21957     {
21958         if(this.singleMode){
21959             return;
21960         }
21961         
21962         var d = new Date(this.viewDate),
21963         year = d.getUTCFullYear(),
21964         month = d.getUTCMonth();
21965         
21966         Roo.each(this.picker().select('.prev', true).elements, function(v){
21967             v.show();
21968             switch (this.viewMode) {
21969                 case 0:
21970
21971                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21972                         v.hide();
21973                     }
21974                     break;
21975                 case 1:
21976                 case 2:
21977                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21978                         v.hide();
21979                     }
21980                     break;
21981             }
21982         });
21983         
21984         Roo.each(this.picker().select('.next', true).elements, function(v){
21985             v.show();
21986             switch (this.viewMode) {
21987                 case 0:
21988
21989                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21990                         v.hide();
21991                     }
21992                     break;
21993                 case 1:
21994                 case 2:
21995                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21996                         v.hide();
21997                     }
21998                     break;
21999             }
22000         })
22001     },
22002     
22003     moveMonth: function(date, dir)
22004     {
22005         if (!dir) {
22006             return date;
22007         }
22008         var new_date = new Date(date.valueOf()),
22009         day = new_date.getUTCDate(),
22010         month = new_date.getUTCMonth(),
22011         mag = Math.abs(dir),
22012         new_month, test;
22013         dir = dir > 0 ? 1 : -1;
22014         if (mag == 1){
22015             test = dir == -1
22016             // If going back one month, make sure month is not current month
22017             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22018             ? function(){
22019                 return new_date.getUTCMonth() == month;
22020             }
22021             // If going forward one month, make sure month is as expected
22022             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22023             : function(){
22024                 return new_date.getUTCMonth() != new_month;
22025             };
22026             new_month = month + dir;
22027             new_date.setUTCMonth(new_month);
22028             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22029             if (new_month < 0 || new_month > 11) {
22030                 new_month = (new_month + 12) % 12;
22031             }
22032         } else {
22033             // For magnitudes >1, move one month at a time...
22034             for (var i=0; i<mag; i++) {
22035                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22036                 new_date = this.moveMonth(new_date, dir);
22037             }
22038             // ...then reset the day, keeping it in the new month
22039             new_month = new_date.getUTCMonth();
22040             new_date.setUTCDate(day);
22041             test = function(){
22042                 return new_month != new_date.getUTCMonth();
22043             };
22044         }
22045         // Common date-resetting loop -- if date is beyond end of month, make it
22046         // end of month
22047         while (test()){
22048             new_date.setUTCDate(--day);
22049             new_date.setUTCMonth(new_month);
22050         }
22051         return new_date;
22052     },
22053
22054     moveYear: function(date, dir)
22055     {
22056         return this.moveMonth(date, dir*12);
22057     },
22058
22059     dateWithinRange: function(date)
22060     {
22061         return date >= this.startDate && date <= this.endDate;
22062     },
22063
22064     
22065     remove: function() 
22066     {
22067         this.picker().remove();
22068     },
22069     
22070     validateValue : function(value)
22071     {
22072         if(this.getVisibilityEl().hasClass('hidden')){
22073             return true;
22074         }
22075         
22076         if(value.length < 1)  {
22077             if(this.allowBlank){
22078                 return true;
22079             }
22080             return false;
22081         }
22082         
22083         if(value.length < this.minLength){
22084             return false;
22085         }
22086         if(value.length > this.maxLength){
22087             return false;
22088         }
22089         if(this.vtype){
22090             var vt = Roo.form.VTypes;
22091             if(!vt[this.vtype](value, this)){
22092                 return false;
22093             }
22094         }
22095         if(typeof this.validator == "function"){
22096             var msg = this.validator(value);
22097             if(msg !== true){
22098                 return false;
22099             }
22100         }
22101         
22102         if(this.regex && !this.regex.test(value)){
22103             return false;
22104         }
22105         
22106         if(typeof(this.parseDate(value)) == 'undefined'){
22107             return false;
22108         }
22109         
22110         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22111             return false;
22112         }      
22113         
22114         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22115             return false;
22116         } 
22117         
22118         
22119         return true;
22120     },
22121     
22122     reset : function()
22123     {
22124         this.date = this.viewDate = '';
22125         
22126         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22127     }
22128    
22129 });
22130
22131 Roo.apply(Roo.bootstrap.DateField,  {
22132     
22133     head : {
22134         tag: 'thead',
22135         cn: [
22136         {
22137             tag: 'tr',
22138             cn: [
22139             {
22140                 tag: 'th',
22141                 cls: 'prev',
22142                 html: '<i class="fa fa-arrow-left"/>'
22143             },
22144             {
22145                 tag: 'th',
22146                 cls: 'switch',
22147                 colspan: '5'
22148             },
22149             {
22150                 tag: 'th',
22151                 cls: 'next',
22152                 html: '<i class="fa fa-arrow-right"/>'
22153             }
22154
22155             ]
22156         }
22157         ]
22158     },
22159     
22160     content : {
22161         tag: 'tbody',
22162         cn: [
22163         {
22164             tag: 'tr',
22165             cn: [
22166             {
22167                 tag: 'td',
22168                 colspan: '7'
22169             }
22170             ]
22171         }
22172         ]
22173     },
22174     
22175     footer : {
22176         tag: 'tfoot',
22177         cn: [
22178         {
22179             tag: 'tr',
22180             cn: [
22181             {
22182                 tag: 'th',
22183                 colspan: '7',
22184                 cls: 'today'
22185             }
22186                     
22187             ]
22188         }
22189         ]
22190     },
22191     
22192     dates:{
22193         en: {
22194             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22195             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22196             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22197             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22198             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22199             today: "Today"
22200         }
22201     },
22202     
22203     modes: [
22204     {
22205         clsName: 'days',
22206         navFnc: 'Month',
22207         navStep: 1
22208     },
22209     {
22210         clsName: 'months',
22211         navFnc: 'FullYear',
22212         navStep: 1
22213     },
22214     {
22215         clsName: 'years',
22216         navFnc: 'FullYear',
22217         navStep: 10
22218     }]
22219 });
22220
22221 Roo.apply(Roo.bootstrap.DateField,  {
22222   
22223     template : {
22224         tag: 'div',
22225         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22226         cn: [
22227         {
22228             tag: 'div',
22229             cls: 'datepicker-days',
22230             cn: [
22231             {
22232                 tag: 'table',
22233                 cls: 'table-condensed',
22234                 cn:[
22235                 Roo.bootstrap.DateField.head,
22236                 {
22237                     tag: 'tbody'
22238                 },
22239                 Roo.bootstrap.DateField.footer
22240                 ]
22241             }
22242             ]
22243         },
22244         {
22245             tag: 'div',
22246             cls: 'datepicker-months',
22247             cn: [
22248             {
22249                 tag: 'table',
22250                 cls: 'table-condensed',
22251                 cn:[
22252                 Roo.bootstrap.DateField.head,
22253                 Roo.bootstrap.DateField.content,
22254                 Roo.bootstrap.DateField.footer
22255                 ]
22256             }
22257             ]
22258         },
22259         {
22260             tag: 'div',
22261             cls: 'datepicker-years',
22262             cn: [
22263             {
22264                 tag: 'table',
22265                 cls: 'table-condensed',
22266                 cn:[
22267                 Roo.bootstrap.DateField.head,
22268                 Roo.bootstrap.DateField.content,
22269                 Roo.bootstrap.DateField.footer
22270                 ]
22271             }
22272             ]
22273         }
22274         ]
22275     }
22276 });
22277
22278  
22279
22280  /*
22281  * - LGPL
22282  *
22283  * TimeField
22284  * 
22285  */
22286
22287 /**
22288  * @class Roo.bootstrap.TimeField
22289  * @extends Roo.bootstrap.Input
22290  * Bootstrap DateField class
22291  * 
22292  * 
22293  * @constructor
22294  * Create a new TimeField
22295  * @param {Object} config The config object
22296  */
22297
22298 Roo.bootstrap.TimeField = function(config){
22299     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22300     this.addEvents({
22301             /**
22302              * @event show
22303              * Fires when this field show.
22304              * @param {Roo.bootstrap.DateField} thisthis
22305              * @param {Mixed} date The date value
22306              */
22307             show : true,
22308             /**
22309              * @event show
22310              * Fires when this field hide.
22311              * @param {Roo.bootstrap.DateField} this
22312              * @param {Mixed} date The date value
22313              */
22314             hide : true,
22315             /**
22316              * @event select
22317              * Fires when select a date.
22318              * @param {Roo.bootstrap.DateField} this
22319              * @param {Mixed} date The date value
22320              */
22321             select : true
22322         });
22323 };
22324
22325 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22326     
22327     /**
22328      * @cfg {String} format
22329      * The default time format string which can be overriden for localization support.  The format must be
22330      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22331      */
22332     format : "H:i",
22333
22334     getAutoCreate : function()
22335     {
22336         this.after = '<i class="fa far fa-clock"></i>';
22337         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22338         
22339          
22340     },
22341     onRender: function(ct, position)
22342     {
22343         
22344         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22345                 
22346         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22347         
22348         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22349         
22350         this.pop = this.picker().select('>.datepicker-time',true).first();
22351         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22352         
22353         this.picker().on('mousedown', this.onMousedown, this);
22354         this.picker().on('click', this.onClick, this);
22355         
22356         this.picker().addClass('datepicker-dropdown');
22357     
22358         this.fillTime();
22359         this.update();
22360             
22361         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22362         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22363         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22364         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22365         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22366         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22367
22368     },
22369     
22370     fireKey: function(e){
22371         if (!this.picker().isVisible()){
22372             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22373                 this.show();
22374             }
22375             return;
22376         }
22377
22378         e.preventDefault();
22379         
22380         switch(e.keyCode){
22381             case 27: // escape
22382                 this.hide();
22383                 break;
22384             case 37: // left
22385             case 39: // right
22386                 this.onTogglePeriod();
22387                 break;
22388             case 38: // up
22389                 this.onIncrementMinutes();
22390                 break;
22391             case 40: // down
22392                 this.onDecrementMinutes();
22393                 break;
22394             case 13: // enter
22395             case 9: // tab
22396                 this.setTime();
22397                 break;
22398         }
22399     },
22400     
22401     onClick: function(e) {
22402         e.stopPropagation();
22403         e.preventDefault();
22404     },
22405     
22406     picker : function()
22407     {
22408         return this.pickerEl;
22409     },
22410     
22411     fillTime: function()
22412     {    
22413         var time = this.pop.select('tbody', true).first();
22414         
22415         time.dom.innerHTML = '';
22416         
22417         time.createChild({
22418             tag: 'tr',
22419             cn: [
22420                 {
22421                     tag: 'td',
22422                     cn: [
22423                         {
22424                             tag: 'a',
22425                             href: '#',
22426                             cls: 'btn',
22427                             cn: [
22428                                 {
22429                                     tag: 'i',
22430                                     cls: 'hours-up fa fas fa-chevron-up'
22431                                 }
22432                             ]
22433                         } 
22434                     ]
22435                 },
22436                 {
22437                     tag: 'td',
22438                     cls: 'separator'
22439                 },
22440                 {
22441                     tag: 'td',
22442                     cn: [
22443                         {
22444                             tag: 'a',
22445                             href: '#',
22446                             cls: 'btn',
22447                             cn: [
22448                                 {
22449                                     tag: 'i',
22450                                     cls: 'minutes-up fa fas fa-chevron-up'
22451                                 }
22452                             ]
22453                         }
22454                     ]
22455                 },
22456                 {
22457                     tag: 'td',
22458                     cls: 'separator'
22459                 }
22460             ]
22461         });
22462         
22463         time.createChild({
22464             tag: 'tr',
22465             cn: [
22466                 {
22467                     tag: 'td',
22468                     cn: [
22469                         {
22470                             tag: 'span',
22471                             cls: 'timepicker-hour',
22472                             html: '00'
22473                         }  
22474                     ]
22475                 },
22476                 {
22477                     tag: 'td',
22478                     cls: 'separator',
22479                     html: ':'
22480                 },
22481                 {
22482                     tag: 'td',
22483                     cn: [
22484                         {
22485                             tag: 'span',
22486                             cls: 'timepicker-minute',
22487                             html: '00'
22488                         }  
22489                     ]
22490                 },
22491                 {
22492                     tag: 'td',
22493                     cls: 'separator'
22494                 },
22495                 {
22496                     tag: 'td',
22497                     cn: [
22498                         {
22499                             tag: 'button',
22500                             type: 'button',
22501                             cls: 'btn btn-primary period',
22502                             html: 'AM'
22503                             
22504                         }
22505                     ]
22506                 }
22507             ]
22508         });
22509         
22510         time.createChild({
22511             tag: 'tr',
22512             cn: [
22513                 {
22514                     tag: 'td',
22515                     cn: [
22516                         {
22517                             tag: 'a',
22518                             href: '#',
22519                             cls: 'btn',
22520                             cn: [
22521                                 {
22522                                     tag: 'span',
22523                                     cls: 'hours-down fa fas fa-chevron-down'
22524                                 }
22525                             ]
22526                         }
22527                     ]
22528                 },
22529                 {
22530                     tag: 'td',
22531                     cls: 'separator'
22532                 },
22533                 {
22534                     tag: 'td',
22535                     cn: [
22536                         {
22537                             tag: 'a',
22538                             href: '#',
22539                             cls: 'btn',
22540                             cn: [
22541                                 {
22542                                     tag: 'span',
22543                                     cls: 'minutes-down fa fas fa-chevron-down'
22544                                 }
22545                             ]
22546                         }
22547                     ]
22548                 },
22549                 {
22550                     tag: 'td',
22551                     cls: 'separator'
22552                 }
22553             ]
22554         });
22555         
22556     },
22557     
22558     update: function()
22559     {
22560         
22561         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22562         
22563         this.fill();
22564     },
22565     
22566     fill: function() 
22567     {
22568         var hours = this.time.getHours();
22569         var minutes = this.time.getMinutes();
22570         var period = 'AM';
22571         
22572         if(hours > 11){
22573             period = 'PM';
22574         }
22575         
22576         if(hours == 0){
22577             hours = 12;
22578         }
22579         
22580         
22581         if(hours > 12){
22582             hours = hours - 12;
22583         }
22584         
22585         if(hours < 10){
22586             hours = '0' + hours;
22587         }
22588         
22589         if(minutes < 10){
22590             minutes = '0' + minutes;
22591         }
22592         
22593         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22594         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22595         this.pop.select('button', true).first().dom.innerHTML = period;
22596         
22597     },
22598     
22599     place: function()
22600     {   
22601         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22602         
22603         var cls = ['bottom'];
22604         
22605         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22606             cls.pop();
22607             cls.push('top');
22608         }
22609         
22610         cls.push('right');
22611         
22612         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22613             cls.pop();
22614             cls.push('left');
22615         }
22616         //this.picker().setXY(20000,20000);
22617         this.picker().addClass(cls.join('-'));
22618         
22619         var _this = this;
22620         
22621         Roo.each(cls, function(c){
22622             if(c == 'bottom'){
22623                 (function() {
22624                  //  
22625                 }).defer(200);
22626                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22627                 //_this.picker().setTop(_this.inputEl().getHeight());
22628                 return;
22629             }
22630             if(c == 'top'){
22631                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22632                 
22633                 //_this.picker().setTop(0 - _this.picker().getHeight());
22634                 return;
22635             }
22636             /*
22637             if(c == 'left'){
22638                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22639                 return;
22640             }
22641             if(c == 'right'){
22642                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22643                 return;
22644             }
22645             */
22646         });
22647         
22648     },
22649   
22650     onFocus : function()
22651     {
22652         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22653         this.show();
22654     },
22655     
22656     onBlur : function()
22657     {
22658         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22659         this.hide();
22660     },
22661     
22662     show : function()
22663     {
22664         this.picker().show();
22665         this.pop.show();
22666         this.update();
22667         this.place();
22668         
22669         this.fireEvent('show', this, this.date);
22670     },
22671     
22672     hide : function()
22673     {
22674         this.picker().hide();
22675         this.pop.hide();
22676         
22677         this.fireEvent('hide', this, this.date);
22678     },
22679     
22680     setTime : function()
22681     {
22682         this.hide();
22683         this.setValue(this.time.format(this.format));
22684         
22685         this.fireEvent('select', this, this.date);
22686         
22687         
22688     },
22689     
22690     onMousedown: function(e){
22691         e.stopPropagation();
22692         e.preventDefault();
22693     },
22694     
22695     onIncrementHours: function()
22696     {
22697         Roo.log('onIncrementHours');
22698         this.time = this.time.add(Date.HOUR, 1);
22699         this.update();
22700         
22701     },
22702     
22703     onDecrementHours: function()
22704     {
22705         Roo.log('onDecrementHours');
22706         this.time = this.time.add(Date.HOUR, -1);
22707         this.update();
22708     },
22709     
22710     onIncrementMinutes: function()
22711     {
22712         Roo.log('onIncrementMinutes');
22713         this.time = this.time.add(Date.MINUTE, 1);
22714         this.update();
22715     },
22716     
22717     onDecrementMinutes: function()
22718     {
22719         Roo.log('onDecrementMinutes');
22720         this.time = this.time.add(Date.MINUTE, -1);
22721         this.update();
22722     },
22723     
22724     onTogglePeriod: function()
22725     {
22726         Roo.log('onTogglePeriod');
22727         this.time = this.time.add(Date.HOUR, 12);
22728         this.update();
22729     }
22730     
22731    
22732 });
22733  
22734
22735 Roo.apply(Roo.bootstrap.TimeField,  {
22736   
22737     template : {
22738         tag: 'div',
22739         cls: 'datepicker dropdown-menu',
22740         cn: [
22741             {
22742                 tag: 'div',
22743                 cls: 'datepicker-time',
22744                 cn: [
22745                 {
22746                     tag: 'table',
22747                     cls: 'table-condensed',
22748                     cn:[
22749                         {
22750                             tag: 'tbody',
22751                             cn: [
22752                                 {
22753                                     tag: 'tr',
22754                                     cn: [
22755                                     {
22756                                         tag: 'td',
22757                                         colspan: '7'
22758                                     }
22759                                     ]
22760                                 }
22761                             ]
22762                         },
22763                         {
22764                             tag: 'tfoot',
22765                             cn: [
22766                                 {
22767                                     tag: 'tr',
22768                                     cn: [
22769                                     {
22770                                         tag: 'th',
22771                                         colspan: '7',
22772                                         cls: '',
22773                                         cn: [
22774                                             {
22775                                                 tag: 'button',
22776                                                 cls: 'btn btn-info ok',
22777                                                 html: 'OK'
22778                                             }
22779                                         ]
22780                                     }
22781                     
22782                                     ]
22783                                 }
22784                             ]
22785                         }
22786                     ]
22787                 }
22788                 ]
22789             }
22790         ]
22791     }
22792 });
22793
22794  
22795
22796  /*
22797  * - LGPL
22798  *
22799  * MonthField
22800  * 
22801  */
22802
22803 /**
22804  * @class Roo.bootstrap.MonthField
22805  * @extends Roo.bootstrap.Input
22806  * Bootstrap MonthField class
22807  * 
22808  * @cfg {String} language default en
22809  * 
22810  * @constructor
22811  * Create a new MonthField
22812  * @param {Object} config The config object
22813  */
22814
22815 Roo.bootstrap.MonthField = function(config){
22816     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22817     
22818     this.addEvents({
22819         /**
22820          * @event show
22821          * Fires when this field show.
22822          * @param {Roo.bootstrap.MonthField} this
22823          * @param {Mixed} date The date value
22824          */
22825         show : true,
22826         /**
22827          * @event show
22828          * Fires when this field hide.
22829          * @param {Roo.bootstrap.MonthField} this
22830          * @param {Mixed} date The date value
22831          */
22832         hide : true,
22833         /**
22834          * @event select
22835          * Fires when select a date.
22836          * @param {Roo.bootstrap.MonthField} this
22837          * @param {String} oldvalue The old value
22838          * @param {String} newvalue The new value
22839          */
22840         select : true
22841     });
22842 };
22843
22844 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22845     
22846     onRender: function(ct, position)
22847     {
22848         
22849         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22850         
22851         this.language = this.language || 'en';
22852         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22853         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22854         
22855         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22856         this.isInline = false;
22857         this.isInput = true;
22858         this.component = this.el.select('.add-on', true).first() || false;
22859         this.component = (this.component && this.component.length === 0) ? false : this.component;
22860         this.hasInput = this.component && this.inputEL().length;
22861         
22862         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22863         
22864         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22865         
22866         this.picker().on('mousedown', this.onMousedown, this);
22867         this.picker().on('click', this.onClick, this);
22868         
22869         this.picker().addClass('datepicker-dropdown');
22870         
22871         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22872             v.setStyle('width', '189px');
22873         });
22874         
22875         this.fillMonths();
22876         
22877         this.update();
22878         
22879         if(this.isInline) {
22880             this.show();
22881         }
22882         
22883     },
22884     
22885     setValue: function(v, suppressEvent)
22886     {   
22887         var o = this.getValue();
22888         
22889         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22890         
22891         this.update();
22892
22893         if(suppressEvent !== true){
22894             this.fireEvent('select', this, o, v);
22895         }
22896         
22897     },
22898     
22899     getValue: function()
22900     {
22901         return this.value;
22902     },
22903     
22904     onClick: function(e) 
22905     {
22906         e.stopPropagation();
22907         e.preventDefault();
22908         
22909         var target = e.getTarget();
22910         
22911         if(target.nodeName.toLowerCase() === 'i'){
22912             target = Roo.get(target).dom.parentNode;
22913         }
22914         
22915         var nodeName = target.nodeName;
22916         var className = target.className;
22917         var html = target.innerHTML;
22918         
22919         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22920             return;
22921         }
22922         
22923         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22924         
22925         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22926         
22927         this.hide();
22928                         
22929     },
22930     
22931     picker : function()
22932     {
22933         return this.pickerEl;
22934     },
22935     
22936     fillMonths: function()
22937     {    
22938         var i = 0;
22939         var months = this.picker().select('>.datepicker-months td', true).first();
22940         
22941         months.dom.innerHTML = '';
22942         
22943         while (i < 12) {
22944             var month = {
22945                 tag: 'span',
22946                 cls: 'month',
22947                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22948             };
22949             
22950             months.createChild(month);
22951         }
22952         
22953     },
22954     
22955     update: function()
22956     {
22957         var _this = this;
22958         
22959         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22960             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22961         }
22962         
22963         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22964             e.removeClass('active');
22965             
22966             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22967                 e.addClass('active');
22968             }
22969         })
22970     },
22971     
22972     place: function()
22973     {
22974         if(this.isInline) {
22975             return;
22976         }
22977         
22978         this.picker().removeClass(['bottom', 'top']);
22979         
22980         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22981             /*
22982              * place to the top of element!
22983              *
22984              */
22985             
22986             this.picker().addClass('top');
22987             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22988             
22989             return;
22990         }
22991         
22992         this.picker().addClass('bottom');
22993         
22994         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22995     },
22996     
22997     onFocus : function()
22998     {
22999         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23000         this.show();
23001     },
23002     
23003     onBlur : function()
23004     {
23005         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23006         
23007         var d = this.inputEl().getValue();
23008         
23009         this.setValue(d);
23010                 
23011         this.hide();
23012     },
23013     
23014     show : function()
23015     {
23016         this.picker().show();
23017         this.picker().select('>.datepicker-months', true).first().show();
23018         this.update();
23019         this.place();
23020         
23021         this.fireEvent('show', this, this.date);
23022     },
23023     
23024     hide : function()
23025     {
23026         if(this.isInline) {
23027             return;
23028         }
23029         this.picker().hide();
23030         this.fireEvent('hide', this, this.date);
23031         
23032     },
23033     
23034     onMousedown: function(e)
23035     {
23036         e.stopPropagation();
23037         e.preventDefault();
23038     },
23039     
23040     keyup: function(e)
23041     {
23042         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23043         this.update();
23044     },
23045
23046     fireKey: function(e)
23047     {
23048         if (!this.picker().isVisible()){
23049             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23050                 this.show();
23051             }
23052             return;
23053         }
23054         
23055         var dir;
23056         
23057         switch(e.keyCode){
23058             case 27: // escape
23059                 this.hide();
23060                 e.preventDefault();
23061                 break;
23062             case 37: // left
23063             case 39: // right
23064                 dir = e.keyCode == 37 ? -1 : 1;
23065                 
23066                 this.vIndex = this.vIndex + dir;
23067                 
23068                 if(this.vIndex < 0){
23069                     this.vIndex = 0;
23070                 }
23071                 
23072                 if(this.vIndex > 11){
23073                     this.vIndex = 11;
23074                 }
23075                 
23076                 if(isNaN(this.vIndex)){
23077                     this.vIndex = 0;
23078                 }
23079                 
23080                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23081                 
23082                 break;
23083             case 38: // up
23084             case 40: // down
23085                 
23086                 dir = e.keyCode == 38 ? -1 : 1;
23087                 
23088                 this.vIndex = this.vIndex + dir * 4;
23089                 
23090                 if(this.vIndex < 0){
23091                     this.vIndex = 0;
23092                 }
23093                 
23094                 if(this.vIndex > 11){
23095                     this.vIndex = 11;
23096                 }
23097                 
23098                 if(isNaN(this.vIndex)){
23099                     this.vIndex = 0;
23100                 }
23101                 
23102                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23103                 break;
23104                 
23105             case 13: // enter
23106                 
23107                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23108                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23109                 }
23110                 
23111                 this.hide();
23112                 e.preventDefault();
23113                 break;
23114             case 9: // tab
23115                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23116                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23117                 }
23118                 this.hide();
23119                 break;
23120             case 16: // shift
23121             case 17: // ctrl
23122             case 18: // alt
23123                 break;
23124             default :
23125                 this.hide();
23126                 
23127         }
23128     },
23129     
23130     remove: function() 
23131     {
23132         this.picker().remove();
23133     }
23134    
23135 });
23136
23137 Roo.apply(Roo.bootstrap.MonthField,  {
23138     
23139     content : {
23140         tag: 'tbody',
23141         cn: [
23142         {
23143             tag: 'tr',
23144             cn: [
23145             {
23146                 tag: 'td',
23147                 colspan: '7'
23148             }
23149             ]
23150         }
23151         ]
23152     },
23153     
23154     dates:{
23155         en: {
23156             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23157             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23158         }
23159     }
23160 });
23161
23162 Roo.apply(Roo.bootstrap.MonthField,  {
23163   
23164     template : {
23165         tag: 'div',
23166         cls: 'datepicker dropdown-menu roo-dynamic',
23167         cn: [
23168             {
23169                 tag: 'div',
23170                 cls: 'datepicker-months',
23171                 cn: [
23172                 {
23173                     tag: 'table',
23174                     cls: 'table-condensed',
23175                     cn:[
23176                         Roo.bootstrap.DateField.content
23177                     ]
23178                 }
23179                 ]
23180             }
23181         ]
23182     }
23183 });
23184
23185  
23186
23187  
23188  /*
23189  * - LGPL
23190  *
23191  * CheckBox
23192  * 
23193  */
23194
23195 /**
23196  * @class Roo.bootstrap.CheckBox
23197  * @extends Roo.bootstrap.Input
23198  * Bootstrap CheckBox class
23199  * 
23200  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23201  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23202  * @cfg {String} boxLabel The text that appears beside the checkbox
23203  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23204  * @cfg {Boolean} checked initnal the element
23205  * @cfg {Boolean} inline inline the element (default false)
23206  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23207  * @cfg {String} tooltip label tooltip
23208  * 
23209  * @constructor
23210  * Create a new CheckBox
23211  * @param {Object} config The config object
23212  */
23213
23214 Roo.bootstrap.CheckBox = function(config){
23215     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23216    
23217     this.addEvents({
23218         /**
23219         * @event check
23220         * Fires when the element is checked or unchecked.
23221         * @param {Roo.bootstrap.CheckBox} this This input
23222         * @param {Boolean} checked The new checked value
23223         */
23224        check : true,
23225        /**
23226         * @event click
23227         * Fires when the element is click.
23228         * @param {Roo.bootstrap.CheckBox} this This input
23229         */
23230        click : true
23231     });
23232     
23233 };
23234
23235 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23236   
23237     inputType: 'checkbox',
23238     inputValue: 1,
23239     valueOff: 0,
23240     boxLabel: false,
23241     checked: false,
23242     weight : false,
23243     inline: false,
23244     tooltip : '',
23245     
23246     // checkbox success does not make any sense really.. 
23247     invalidClass : "",
23248     validClass : "",
23249     
23250     
23251     getAutoCreate : function()
23252     {
23253         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23254         
23255         var id = Roo.id();
23256         
23257         var cfg = {};
23258         
23259         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23260         
23261         if(this.inline){
23262             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23263         }
23264         
23265         var input =  {
23266             tag: 'input',
23267             id : id,
23268             type : this.inputType,
23269             value : this.inputValue,
23270             cls : 'roo-' + this.inputType, //'form-box',
23271             placeholder : this.placeholder || ''
23272             
23273         };
23274         
23275         if(this.inputType != 'radio'){
23276             var hidden =  {
23277                 tag: 'input',
23278                 type : 'hidden',
23279                 cls : 'roo-hidden-value',
23280                 value : this.checked ? this.inputValue : this.valueOff
23281             };
23282         }
23283         
23284             
23285         if (this.weight) { // Validity check?
23286             cfg.cls += " " + this.inputType + "-" + this.weight;
23287         }
23288         
23289         if (this.disabled) {
23290             input.disabled=true;
23291         }
23292         
23293         if(this.checked){
23294             input.checked = this.checked;
23295         }
23296         
23297         if (this.name) {
23298             
23299             input.name = this.name;
23300             
23301             if(this.inputType != 'radio'){
23302                 hidden.name = this.name;
23303                 input.name = '_hidden_' + this.name;
23304             }
23305         }
23306         
23307         if (this.size) {
23308             input.cls += ' input-' + this.size;
23309         }
23310         
23311         var settings=this;
23312         
23313         ['xs','sm','md','lg'].map(function(size){
23314             if (settings[size]) {
23315                 cfg.cls += ' col-' + size + '-' + settings[size];
23316             }
23317         });
23318         
23319         var inputblock = input;
23320          
23321         if (this.before || this.after) {
23322             
23323             inputblock = {
23324                 cls : 'input-group',
23325                 cn :  [] 
23326             };
23327             
23328             if (this.before) {
23329                 inputblock.cn.push({
23330                     tag :'span',
23331                     cls : 'input-group-addon',
23332                     html : this.before
23333                 });
23334             }
23335             
23336             inputblock.cn.push(input);
23337             
23338             if(this.inputType != 'radio'){
23339                 inputblock.cn.push(hidden);
23340             }
23341             
23342             if (this.after) {
23343                 inputblock.cn.push({
23344                     tag :'span',
23345                     cls : 'input-group-addon',
23346                     html : this.after
23347                 });
23348             }
23349             
23350         }
23351         var boxLabelCfg = false;
23352         
23353         if(this.boxLabel){
23354            
23355             boxLabelCfg = {
23356                 tag: 'label',
23357                 //'for': id, // box label is handled by onclick - so no for...
23358                 cls: 'box-label',
23359                 html: this.boxLabel
23360             };
23361             if(this.tooltip){
23362                 boxLabelCfg.tooltip = this.tooltip;
23363             }
23364              
23365         }
23366         
23367         
23368         if (align ==='left' && this.fieldLabel.length) {
23369 //                Roo.log("left and has label");
23370             cfg.cn = [
23371                 {
23372                     tag: 'label',
23373                     'for' :  id,
23374                     cls : 'control-label',
23375                     html : this.fieldLabel
23376                 },
23377                 {
23378                     cls : "", 
23379                     cn: [
23380                         inputblock
23381                     ]
23382                 }
23383             ];
23384             
23385             if (boxLabelCfg) {
23386                 cfg.cn[1].cn.push(boxLabelCfg);
23387             }
23388             
23389             if(this.labelWidth > 12){
23390                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23391             }
23392             
23393             if(this.labelWidth < 13 && this.labelmd == 0){
23394                 this.labelmd = this.labelWidth;
23395             }
23396             
23397             if(this.labellg > 0){
23398                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23399                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23400             }
23401             
23402             if(this.labelmd > 0){
23403                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23404                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23405             }
23406             
23407             if(this.labelsm > 0){
23408                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23409                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23410             }
23411             
23412             if(this.labelxs > 0){
23413                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23414                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23415             }
23416             
23417         } else if ( this.fieldLabel.length) {
23418 //                Roo.log(" label");
23419                 cfg.cn = [
23420                    
23421                     {
23422                         tag: this.boxLabel ? 'span' : 'label',
23423                         'for': id,
23424                         cls: 'control-label box-input-label',
23425                         //cls : 'input-group-addon',
23426                         html : this.fieldLabel
23427                     },
23428                     
23429                     inputblock
23430                     
23431                 ];
23432                 if (boxLabelCfg) {
23433                     cfg.cn.push(boxLabelCfg);
23434                 }
23435
23436         } else {
23437             
23438 //                Roo.log(" no label && no align");
23439                 cfg.cn = [  inputblock ] ;
23440                 if (boxLabelCfg) {
23441                     cfg.cn.push(boxLabelCfg);
23442                 }
23443
23444                 
23445         }
23446         
23447        
23448         
23449         if(this.inputType != 'radio'){
23450             cfg.cn.push(hidden);
23451         }
23452         
23453         return cfg;
23454         
23455     },
23456     
23457     /**
23458      * return the real input element.
23459      */
23460     inputEl: function ()
23461     {
23462         return this.el.select('input.roo-' + this.inputType,true).first();
23463     },
23464     hiddenEl: function ()
23465     {
23466         return this.el.select('input.roo-hidden-value',true).first();
23467     },
23468     
23469     labelEl: function()
23470     {
23471         return this.el.select('label.control-label',true).first();
23472     },
23473     /* depricated... */
23474     
23475     label: function()
23476     {
23477         return this.labelEl();
23478     },
23479     
23480     boxLabelEl: function()
23481     {
23482         return this.el.select('label.box-label',true).first();
23483     },
23484     
23485     initEvents : function()
23486     {
23487 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23488         
23489         this.inputEl().on('click', this.onClick,  this);
23490         
23491         if (this.boxLabel) { 
23492             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23493         }
23494         
23495         this.startValue = this.getValue();
23496         
23497         if(this.groupId){
23498             Roo.bootstrap.CheckBox.register(this);
23499         }
23500     },
23501     
23502     onClick : function(e)
23503     {   
23504         if(this.fireEvent('click', this, e) !== false){
23505             this.setChecked(!this.checked);
23506         }
23507         
23508     },
23509     
23510     setChecked : function(state,suppressEvent)
23511     {
23512         this.startValue = this.getValue();
23513
23514         if(this.inputType == 'radio'){
23515             
23516             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23517                 e.dom.checked = false;
23518             });
23519             
23520             this.inputEl().dom.checked = true;
23521             
23522             this.inputEl().dom.value = this.inputValue;
23523             
23524             if(suppressEvent !== true){
23525                 this.fireEvent('check', this, true);
23526             }
23527             
23528             this.validate();
23529             
23530             return;
23531         }
23532         
23533         this.checked = state;
23534         
23535         this.inputEl().dom.checked = state;
23536         
23537         
23538         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23539         
23540         if(suppressEvent !== true){
23541             this.fireEvent('check', this, state);
23542         }
23543         
23544         this.validate();
23545     },
23546     
23547     getValue : function()
23548     {
23549         if(this.inputType == 'radio'){
23550             return this.getGroupValue();
23551         }
23552         
23553         return this.hiddenEl().dom.value;
23554         
23555     },
23556     
23557     getGroupValue : function()
23558     {
23559         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23560             return '';
23561         }
23562         
23563         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23564     },
23565     
23566     setValue : function(v,suppressEvent)
23567     {
23568         if(this.inputType == 'radio'){
23569             this.setGroupValue(v, suppressEvent);
23570             return;
23571         }
23572         
23573         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23574         
23575         this.validate();
23576     },
23577     
23578     setGroupValue : function(v, suppressEvent)
23579     {
23580         this.startValue = this.getValue();
23581         
23582         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23583             e.dom.checked = false;
23584             
23585             if(e.dom.value == v){
23586                 e.dom.checked = true;
23587             }
23588         });
23589         
23590         if(suppressEvent !== true){
23591             this.fireEvent('check', this, true);
23592         }
23593
23594         this.validate();
23595         
23596         return;
23597     },
23598     
23599     validate : function()
23600     {
23601         if(this.getVisibilityEl().hasClass('hidden')){
23602             return true;
23603         }
23604         
23605         if(
23606                 this.disabled || 
23607                 (this.inputType == 'radio' && this.validateRadio()) ||
23608                 (this.inputType == 'checkbox' && this.validateCheckbox())
23609         ){
23610             this.markValid();
23611             return true;
23612         }
23613         
23614         this.markInvalid();
23615         return false;
23616     },
23617     
23618     validateRadio : function()
23619     {
23620         if(this.getVisibilityEl().hasClass('hidden')){
23621             return true;
23622         }
23623         
23624         if(this.allowBlank){
23625             return true;
23626         }
23627         
23628         var valid = false;
23629         
23630         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23631             if(!e.dom.checked){
23632                 return;
23633             }
23634             
23635             valid = true;
23636             
23637             return false;
23638         });
23639         
23640         return valid;
23641     },
23642     
23643     validateCheckbox : function()
23644     {
23645         if(!this.groupId){
23646             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23647             //return (this.getValue() == this.inputValue) ? true : false;
23648         }
23649         
23650         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23651         
23652         if(!group){
23653             return false;
23654         }
23655         
23656         var r = false;
23657         
23658         for(var i in group){
23659             if(group[i].el.isVisible(true)){
23660                 r = false;
23661                 break;
23662             }
23663             
23664             r = true;
23665         }
23666         
23667         for(var i in group){
23668             if(r){
23669                 break;
23670             }
23671             
23672             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23673         }
23674         
23675         return r;
23676     },
23677     
23678     /**
23679      * Mark this field as valid
23680      */
23681     markValid : function()
23682     {
23683         var _this = this;
23684         
23685         this.fireEvent('valid', this);
23686         
23687         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23688         
23689         if(this.groupId){
23690             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23691         }
23692         
23693         if(label){
23694             label.markValid();
23695         }
23696
23697         if(this.inputType == 'radio'){
23698             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23699                 var fg = e.findParent('.form-group', false, true);
23700                 if (Roo.bootstrap.version == 3) {
23701                     fg.removeClass([_this.invalidClass, _this.validClass]);
23702                     fg.addClass(_this.validClass);
23703                 } else {
23704                     fg.removeClass(['is-valid', 'is-invalid']);
23705                     fg.addClass('is-valid');
23706                 }
23707             });
23708             
23709             return;
23710         }
23711
23712         if(!this.groupId){
23713             var fg = this.el.findParent('.form-group', false, true);
23714             if (Roo.bootstrap.version == 3) {
23715                 fg.removeClass([this.invalidClass, this.validClass]);
23716                 fg.addClass(this.validClass);
23717             } else {
23718                 fg.removeClass(['is-valid', 'is-invalid']);
23719                 fg.addClass('is-valid');
23720             }
23721             return;
23722         }
23723         
23724         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23725         
23726         if(!group){
23727             return;
23728         }
23729         
23730         for(var i in group){
23731             var fg = group[i].el.findParent('.form-group', false, true);
23732             if (Roo.bootstrap.version == 3) {
23733                 fg.removeClass([this.invalidClass, this.validClass]);
23734                 fg.addClass(this.validClass);
23735             } else {
23736                 fg.removeClass(['is-valid', 'is-invalid']);
23737                 fg.addClass('is-valid');
23738             }
23739         }
23740     },
23741     
23742      /**
23743      * Mark this field as invalid
23744      * @param {String} msg The validation message
23745      */
23746     markInvalid : function(msg)
23747     {
23748         if(this.allowBlank){
23749             return;
23750         }
23751         
23752         var _this = this;
23753         
23754         this.fireEvent('invalid', this, msg);
23755         
23756         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23757         
23758         if(this.groupId){
23759             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23760         }
23761         
23762         if(label){
23763             label.markInvalid();
23764         }
23765             
23766         if(this.inputType == 'radio'){
23767             
23768             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23769                 var fg = e.findParent('.form-group', false, true);
23770                 if (Roo.bootstrap.version == 3) {
23771                     fg.removeClass([_this.invalidClass, _this.validClass]);
23772                     fg.addClass(_this.invalidClass);
23773                 } else {
23774                     fg.removeClass(['is-invalid', 'is-valid']);
23775                     fg.addClass('is-invalid');
23776                 }
23777             });
23778             
23779             return;
23780         }
23781         
23782         if(!this.groupId){
23783             var fg = this.el.findParent('.form-group', false, true);
23784             if (Roo.bootstrap.version == 3) {
23785                 fg.removeClass([_this.invalidClass, _this.validClass]);
23786                 fg.addClass(_this.invalidClass);
23787             } else {
23788                 fg.removeClass(['is-invalid', 'is-valid']);
23789                 fg.addClass('is-invalid');
23790             }
23791             return;
23792         }
23793         
23794         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23795         
23796         if(!group){
23797             return;
23798         }
23799         
23800         for(var i in group){
23801             var fg = group[i].el.findParent('.form-group', false, true);
23802             if (Roo.bootstrap.version == 3) {
23803                 fg.removeClass([_this.invalidClass, _this.validClass]);
23804                 fg.addClass(_this.invalidClass);
23805             } else {
23806                 fg.removeClass(['is-invalid', 'is-valid']);
23807                 fg.addClass('is-invalid');
23808             }
23809         }
23810         
23811     },
23812     
23813     clearInvalid : function()
23814     {
23815         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23816         
23817         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23818         
23819         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23820         
23821         if (label && label.iconEl) {
23822             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23823             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23824         }
23825     },
23826     
23827     disable : function()
23828     {
23829         if(this.inputType != 'radio'){
23830             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23831             return;
23832         }
23833         
23834         var _this = this;
23835         
23836         if(this.rendered){
23837             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23838                 _this.getActionEl().addClass(this.disabledClass);
23839                 e.dom.disabled = true;
23840             });
23841         }
23842         
23843         this.disabled = true;
23844         this.fireEvent("disable", this);
23845         return this;
23846     },
23847
23848     enable : function()
23849     {
23850         if(this.inputType != 'radio'){
23851             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23852             return;
23853         }
23854         
23855         var _this = this;
23856         
23857         if(this.rendered){
23858             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23859                 _this.getActionEl().removeClass(this.disabledClass);
23860                 e.dom.disabled = false;
23861             });
23862         }
23863         
23864         this.disabled = false;
23865         this.fireEvent("enable", this);
23866         return this;
23867     },
23868     
23869     setBoxLabel : function(v)
23870     {
23871         this.boxLabel = v;
23872         
23873         if(this.rendered){
23874             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23875         }
23876     }
23877
23878 });
23879
23880 Roo.apply(Roo.bootstrap.CheckBox, {
23881     
23882     groups: {},
23883     
23884      /**
23885     * register a CheckBox Group
23886     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23887     */
23888     register : function(checkbox)
23889     {
23890         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23891             this.groups[checkbox.groupId] = {};
23892         }
23893         
23894         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23895             return;
23896         }
23897         
23898         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23899         
23900     },
23901     /**
23902     * fetch a CheckBox Group based on the group ID
23903     * @param {string} the group ID
23904     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23905     */
23906     get: function(groupId) {
23907         if (typeof(this.groups[groupId]) == 'undefined') {
23908             return false;
23909         }
23910         
23911         return this.groups[groupId] ;
23912     }
23913     
23914     
23915 });
23916 /*
23917  * - LGPL
23918  *
23919  * RadioItem
23920  * 
23921  */
23922
23923 /**
23924  * @class Roo.bootstrap.Radio
23925  * @extends Roo.bootstrap.Component
23926  * Bootstrap Radio class
23927  * @cfg {String} boxLabel - the label associated
23928  * @cfg {String} value - the value of radio
23929  * 
23930  * @constructor
23931  * Create a new Radio
23932  * @param {Object} config The config object
23933  */
23934 Roo.bootstrap.Radio = function(config){
23935     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23936     
23937 };
23938
23939 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23940     
23941     boxLabel : '',
23942     
23943     value : '',
23944     
23945     getAutoCreate : function()
23946     {
23947         var cfg = {
23948             tag : 'div',
23949             cls : 'form-group radio',
23950             cn : [
23951                 {
23952                     tag : 'label',
23953                     cls : 'box-label',
23954                     html : this.boxLabel
23955                 }
23956             ]
23957         };
23958         
23959         return cfg;
23960     },
23961     
23962     initEvents : function() 
23963     {
23964         this.parent().register(this);
23965         
23966         this.el.on('click', this.onClick, this);
23967         
23968     },
23969     
23970     onClick : function(e)
23971     {
23972         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23973             this.setChecked(true);
23974         }
23975     },
23976     
23977     setChecked : function(state, suppressEvent)
23978     {
23979         this.parent().setValue(this.value, suppressEvent);
23980         
23981     },
23982     
23983     setBoxLabel : function(v)
23984     {
23985         this.boxLabel = v;
23986         
23987         if(this.rendered){
23988             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23989         }
23990     }
23991     
23992 });
23993  
23994
23995  /*
23996  * - LGPL
23997  *
23998  * Input
23999  * 
24000  */
24001
24002 /**
24003  * @class Roo.bootstrap.SecurePass
24004  * @extends Roo.bootstrap.Input
24005  * Bootstrap SecurePass class
24006  *
24007  * 
24008  * @constructor
24009  * Create a new SecurePass
24010  * @param {Object} config The config object
24011  */
24012  
24013 Roo.bootstrap.SecurePass = function (config) {
24014     // these go here, so the translation tool can replace them..
24015     this.errors = {
24016         PwdEmpty: "Please type a password, and then retype it to confirm.",
24017         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24018         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24019         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24020         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24021         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24022         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24023         TooWeak: "Your password is Too Weak."
24024     },
24025     this.meterLabel = "Password strength:";
24026     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24027     this.meterClass = [
24028         "roo-password-meter-tooweak", 
24029         "roo-password-meter-weak", 
24030         "roo-password-meter-medium", 
24031         "roo-password-meter-strong", 
24032         "roo-password-meter-grey"
24033     ];
24034     
24035     this.errors = {};
24036     
24037     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24038 }
24039
24040 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24041     /**
24042      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24043      * {
24044      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24045      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24046      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24047      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24048      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24049      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24050      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24051      * })
24052      */
24053     // private
24054     
24055     meterWidth: 300,
24056     errorMsg :'',    
24057     errors: false,
24058     imageRoot: '/',
24059     /**
24060      * @cfg {String/Object} Label for the strength meter (defaults to
24061      * 'Password strength:')
24062      */
24063     // private
24064     meterLabel: '',
24065     /**
24066      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24067      * ['Weak', 'Medium', 'Strong'])
24068      */
24069     // private    
24070     pwdStrengths: false,    
24071     // private
24072     strength: 0,
24073     // private
24074     _lastPwd: null,
24075     // private
24076     kCapitalLetter: 0,
24077     kSmallLetter: 1,
24078     kDigit: 2,
24079     kPunctuation: 3,
24080     
24081     insecure: false,
24082     // private
24083     initEvents: function ()
24084     {
24085         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24086
24087         if (this.el.is('input[type=password]') && Roo.isSafari) {
24088             this.el.on('keydown', this.SafariOnKeyDown, this);
24089         }
24090
24091         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24092     },
24093     // private
24094     onRender: function (ct, position)
24095     {
24096         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24097         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24098         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24099
24100         this.trigger.createChild({
24101                    cn: [
24102                     {
24103                     //id: 'PwdMeter',
24104                     tag: 'div',
24105                     cls: 'roo-password-meter-grey col-xs-12',
24106                     style: {
24107                         //width: 0,
24108                         //width: this.meterWidth + 'px'                                                
24109                         }
24110                     },
24111                     {                            
24112                          cls: 'roo-password-meter-text'                          
24113                     }
24114                 ]            
24115         });
24116
24117          
24118         if (this.hideTrigger) {
24119             this.trigger.setDisplayed(false);
24120         }
24121         this.setSize(this.width || '', this.height || '');
24122     },
24123     // private
24124     onDestroy: function ()
24125     {
24126         if (this.trigger) {
24127             this.trigger.removeAllListeners();
24128             this.trigger.remove();
24129         }
24130         if (this.wrap) {
24131             this.wrap.remove();
24132         }
24133         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24134     },
24135     // private
24136     checkStrength: function ()
24137     {
24138         var pwd = this.inputEl().getValue();
24139         if (pwd == this._lastPwd) {
24140             return;
24141         }
24142
24143         var strength;
24144         if (this.ClientSideStrongPassword(pwd)) {
24145             strength = 3;
24146         } else if (this.ClientSideMediumPassword(pwd)) {
24147             strength = 2;
24148         } else if (this.ClientSideWeakPassword(pwd)) {
24149             strength = 1;
24150         } else {
24151             strength = 0;
24152         }
24153         
24154         Roo.log('strength1: ' + strength);
24155         
24156         //var pm = this.trigger.child('div/div/div').dom;
24157         var pm = this.trigger.child('div/div');
24158         pm.removeClass(this.meterClass);
24159         pm.addClass(this.meterClass[strength]);
24160                 
24161         
24162         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24163                 
24164         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24165         
24166         this._lastPwd = pwd;
24167     },
24168     reset: function ()
24169     {
24170         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24171         
24172         this._lastPwd = '';
24173         
24174         var pm = this.trigger.child('div/div');
24175         pm.removeClass(this.meterClass);
24176         pm.addClass('roo-password-meter-grey');        
24177         
24178         
24179         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24180         
24181         pt.innerHTML = '';
24182         this.inputEl().dom.type='password';
24183     },
24184     // private
24185     validateValue: function (value)
24186     {
24187         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24188             return false;
24189         }
24190         if (value.length == 0) {
24191             if (this.allowBlank) {
24192                 this.clearInvalid();
24193                 return true;
24194             }
24195
24196             this.markInvalid(this.errors.PwdEmpty);
24197             this.errorMsg = this.errors.PwdEmpty;
24198             return false;
24199         }
24200         
24201         if(this.insecure){
24202             return true;
24203         }
24204         
24205         if (!value.match(/[\x21-\x7e]+/)) {
24206             this.markInvalid(this.errors.PwdBadChar);
24207             this.errorMsg = this.errors.PwdBadChar;
24208             return false;
24209         }
24210         if (value.length < 6) {
24211             this.markInvalid(this.errors.PwdShort);
24212             this.errorMsg = this.errors.PwdShort;
24213             return false;
24214         }
24215         if (value.length > 16) {
24216             this.markInvalid(this.errors.PwdLong);
24217             this.errorMsg = this.errors.PwdLong;
24218             return false;
24219         }
24220         var strength;
24221         if (this.ClientSideStrongPassword(value)) {
24222             strength = 3;
24223         } else if (this.ClientSideMediumPassword(value)) {
24224             strength = 2;
24225         } else if (this.ClientSideWeakPassword(value)) {
24226             strength = 1;
24227         } else {
24228             strength = 0;
24229         }
24230
24231         
24232         if (strength < 2) {
24233             //this.markInvalid(this.errors.TooWeak);
24234             this.errorMsg = this.errors.TooWeak;
24235             //return false;
24236         }
24237         
24238         
24239         console.log('strength2: ' + strength);
24240         
24241         //var pm = this.trigger.child('div/div/div').dom;
24242         
24243         var pm = this.trigger.child('div/div');
24244         pm.removeClass(this.meterClass);
24245         pm.addClass(this.meterClass[strength]);
24246                 
24247         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24248                 
24249         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24250         
24251         this.errorMsg = ''; 
24252         return true;
24253     },
24254     // private
24255     CharacterSetChecks: function (type)
24256     {
24257         this.type = type;
24258         this.fResult = false;
24259     },
24260     // private
24261     isctype: function (character, type)
24262     {
24263         switch (type) {  
24264             case this.kCapitalLetter:
24265                 if (character >= 'A' && character <= 'Z') {
24266                     return true;
24267                 }
24268                 break;
24269             
24270             case this.kSmallLetter:
24271                 if (character >= 'a' && character <= 'z') {
24272                     return true;
24273                 }
24274                 break;
24275             
24276             case this.kDigit:
24277                 if (character >= '0' && character <= '9') {
24278                     return true;
24279                 }
24280                 break;
24281             
24282             case this.kPunctuation:
24283                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24284                     return true;
24285                 }
24286                 break;
24287             
24288             default:
24289                 return false;
24290         }
24291
24292     },
24293     // private
24294     IsLongEnough: function (pwd, size)
24295     {
24296         return !(pwd == null || isNaN(size) || pwd.length < size);
24297     },
24298     // private
24299     SpansEnoughCharacterSets: function (word, nb)
24300     {
24301         if (!this.IsLongEnough(word, nb))
24302         {
24303             return false;
24304         }
24305
24306         var characterSetChecks = new Array(
24307             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24308             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24309         );
24310         
24311         for (var index = 0; index < word.length; ++index) {
24312             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24313                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24314                     characterSetChecks[nCharSet].fResult = true;
24315                     break;
24316                 }
24317             }
24318         }
24319
24320         var nCharSets = 0;
24321         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24322             if (characterSetChecks[nCharSet].fResult) {
24323                 ++nCharSets;
24324             }
24325         }
24326
24327         if (nCharSets < nb) {
24328             return false;
24329         }
24330         return true;
24331     },
24332     // private
24333     ClientSideStrongPassword: function (pwd)
24334     {
24335         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24336     },
24337     // private
24338     ClientSideMediumPassword: function (pwd)
24339     {
24340         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24341     },
24342     // private
24343     ClientSideWeakPassword: function (pwd)
24344     {
24345         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24346     }
24347           
24348 })//<script type="text/javascript">
24349
24350 /*
24351  * Based  Ext JS Library 1.1.1
24352  * Copyright(c) 2006-2007, Ext JS, LLC.
24353  * LGPL
24354  *
24355  */
24356  
24357 /**
24358  * @class Roo.HtmlEditorCore
24359  * @extends Roo.Component
24360  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24361  *
24362  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24363  */
24364
24365 Roo.HtmlEditorCore = function(config){
24366     
24367     
24368     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24369     
24370     
24371     this.addEvents({
24372         /**
24373          * @event initialize
24374          * Fires when the editor is fully initialized (including the iframe)
24375          * @param {Roo.HtmlEditorCore} this
24376          */
24377         initialize: true,
24378         /**
24379          * @event activate
24380          * Fires when the editor is first receives the focus. Any insertion must wait
24381          * until after this event.
24382          * @param {Roo.HtmlEditorCore} this
24383          */
24384         activate: true,
24385          /**
24386          * @event beforesync
24387          * Fires before the textarea is updated with content from the editor iframe. Return false
24388          * to cancel the sync.
24389          * @param {Roo.HtmlEditorCore} this
24390          * @param {String} html
24391          */
24392         beforesync: true,
24393          /**
24394          * @event beforepush
24395          * Fires before the iframe editor is updated with content from the textarea. Return false
24396          * to cancel the push.
24397          * @param {Roo.HtmlEditorCore} this
24398          * @param {String} html
24399          */
24400         beforepush: true,
24401          /**
24402          * @event sync
24403          * Fires when the textarea is updated with content from the editor iframe.
24404          * @param {Roo.HtmlEditorCore} this
24405          * @param {String} html
24406          */
24407         sync: true,
24408          /**
24409          * @event push
24410          * Fires when the iframe editor is updated with content from the textarea.
24411          * @param {Roo.HtmlEditorCore} this
24412          * @param {String} html
24413          */
24414         push: true,
24415         
24416         /**
24417          * @event editorevent
24418          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24419          * @param {Roo.HtmlEditorCore} this
24420          */
24421         editorevent: true
24422         
24423     });
24424     
24425     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24426     
24427     // defaults : white / black...
24428     this.applyBlacklists();
24429     
24430     
24431     
24432 };
24433
24434
24435 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24436
24437
24438      /**
24439      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24440      */
24441     
24442     owner : false,
24443     
24444      /**
24445      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24446      *                        Roo.resizable.
24447      */
24448     resizable : false,
24449      /**
24450      * @cfg {Number} height (in pixels)
24451      */   
24452     height: 300,
24453    /**
24454      * @cfg {Number} width (in pixels)
24455      */   
24456     width: 500,
24457     
24458     /**
24459      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24460      * 
24461      */
24462     stylesheets: false,
24463     
24464     // id of frame..
24465     frameId: false,
24466     
24467     // private properties
24468     validationEvent : false,
24469     deferHeight: true,
24470     initialized : false,
24471     activated : false,
24472     sourceEditMode : false,
24473     onFocus : Roo.emptyFn,
24474     iframePad:3,
24475     hideMode:'offsets',
24476     
24477     clearUp: true,
24478     
24479     // blacklist + whitelisted elements..
24480     black: false,
24481     white: false,
24482      
24483     bodyCls : '',
24484
24485     /**
24486      * Protected method that will not generally be called directly. It
24487      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24488      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24489      */
24490     getDocMarkup : function(){
24491         // body styles..
24492         var st = '';
24493         
24494         // inherit styels from page...?? 
24495         if (this.stylesheets === false) {
24496             
24497             Roo.get(document.head).select('style').each(function(node) {
24498                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24499             });
24500             
24501             Roo.get(document.head).select('link').each(function(node) { 
24502                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24503             });
24504             
24505         } else if (!this.stylesheets.length) {
24506                 // simple..
24507                 st = '<style type="text/css">' +
24508                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24509                    '</style>';
24510         } else {
24511             for (var i in this.stylesheets) { 
24512                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24513             }
24514             
24515         }
24516         
24517         st +=  '<style type="text/css">' +
24518             'IMG { cursor: pointer } ' +
24519         '</style>';
24520
24521         var cls = 'roo-htmleditor-body';
24522         
24523         if(this.bodyCls.length){
24524             cls += ' ' + this.bodyCls;
24525         }
24526         
24527         return '<html><head>' + st  +
24528             //<style type="text/css">' +
24529             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24530             //'</style>' +
24531             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24532     },
24533
24534     // private
24535     onRender : function(ct, position)
24536     {
24537         var _t = this;
24538         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24539         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24540         
24541         
24542         this.el.dom.style.border = '0 none';
24543         this.el.dom.setAttribute('tabIndex', -1);
24544         this.el.addClass('x-hidden hide');
24545         
24546         
24547         
24548         if(Roo.isIE){ // fix IE 1px bogus margin
24549             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24550         }
24551        
24552         
24553         this.frameId = Roo.id();
24554         
24555          
24556         
24557         var iframe = this.owner.wrap.createChild({
24558             tag: 'iframe',
24559             cls: 'form-control', // bootstrap..
24560             id: this.frameId,
24561             name: this.frameId,
24562             frameBorder : 'no',
24563             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24564         }, this.el
24565         );
24566         
24567         
24568         this.iframe = iframe.dom;
24569
24570          this.assignDocWin();
24571         
24572         this.doc.designMode = 'on';
24573        
24574         this.doc.open();
24575         this.doc.write(this.getDocMarkup());
24576         this.doc.close();
24577
24578         
24579         var task = { // must defer to wait for browser to be ready
24580             run : function(){
24581                 //console.log("run task?" + this.doc.readyState);
24582                 this.assignDocWin();
24583                 if(this.doc.body || this.doc.readyState == 'complete'){
24584                     try {
24585                         this.doc.designMode="on";
24586                     } catch (e) {
24587                         return;
24588                     }
24589                     Roo.TaskMgr.stop(task);
24590                     this.initEditor.defer(10, this);
24591                 }
24592             },
24593             interval : 10,
24594             duration: 10000,
24595             scope: this
24596         };
24597         Roo.TaskMgr.start(task);
24598
24599     },
24600
24601     // private
24602     onResize : function(w, h)
24603     {
24604          Roo.log('resize: ' +w + ',' + h );
24605         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24606         if(!this.iframe){
24607             return;
24608         }
24609         if(typeof w == 'number'){
24610             
24611             this.iframe.style.width = w + 'px';
24612         }
24613         if(typeof h == 'number'){
24614             
24615             this.iframe.style.height = h + 'px';
24616             if(this.doc){
24617                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24618             }
24619         }
24620         
24621     },
24622
24623     /**
24624      * Toggles the editor between standard and source edit mode.
24625      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24626      */
24627     toggleSourceEdit : function(sourceEditMode){
24628         
24629         this.sourceEditMode = sourceEditMode === true;
24630         
24631         if(this.sourceEditMode){
24632  
24633             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24634             
24635         }else{
24636             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24637             //this.iframe.className = '';
24638             this.deferFocus();
24639         }
24640         //this.setSize(this.owner.wrap.getSize());
24641         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24642     },
24643
24644     
24645   
24646
24647     /**
24648      * Protected method that will not generally be called directly. If you need/want
24649      * custom HTML cleanup, this is the method you should override.
24650      * @param {String} html The HTML to be cleaned
24651      * return {String} The cleaned HTML
24652      */
24653     cleanHtml : function(html){
24654         html = String(html);
24655         if(html.length > 5){
24656             if(Roo.isSafari){ // strip safari nonsense
24657                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24658             }
24659         }
24660         if(html == '&nbsp;'){
24661             html = '';
24662         }
24663         return html;
24664     },
24665
24666     /**
24667      * HTML Editor -> Textarea
24668      * Protected method that will not generally be called directly. Syncs the contents
24669      * of the editor iframe with the textarea.
24670      */
24671     syncValue : function(){
24672         if(this.initialized){
24673             var bd = (this.doc.body || this.doc.documentElement);
24674             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24675             var html = bd.innerHTML;
24676             if(Roo.isSafari){
24677                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24678                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24679                 if(m && m[1]){
24680                     html = '<div style="'+m[0]+'">' + html + '</div>';
24681                 }
24682             }
24683             html = this.cleanHtml(html);
24684             // fix up the special chars.. normaly like back quotes in word...
24685             // however we do not want to do this with chinese..
24686             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24687                 
24688                 var cc = match.charCodeAt();
24689
24690                 // Get the character value, handling surrogate pairs
24691                 if (match.length == 2) {
24692                     // It's a surrogate pair, calculate the Unicode code point
24693                     var high = match.charCodeAt(0) - 0xD800;
24694                     var low  = match.charCodeAt(1) - 0xDC00;
24695                     cc = (high * 0x400) + low + 0x10000;
24696                 }  else if (
24697                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24698                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24699                     (cc >= 0xf900 && cc < 0xfb00 )
24700                 ) {
24701                         return match;
24702                 }  
24703          
24704                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24705                 return "&#" + cc + ";";
24706                 
24707                 
24708             });
24709             
24710             
24711              
24712             if(this.owner.fireEvent('beforesync', this, html) !== false){
24713                 this.el.dom.value = html;
24714                 this.owner.fireEvent('sync', this, html);
24715             }
24716         }
24717     },
24718
24719     /**
24720      * Protected method that will not generally be called directly. Pushes the value of the textarea
24721      * into the iframe editor.
24722      */
24723     pushValue : function(){
24724         if(this.initialized){
24725             var v = this.el.dom.value.trim();
24726             
24727 //            if(v.length < 1){
24728 //                v = '&#160;';
24729 //            }
24730             
24731             if(this.owner.fireEvent('beforepush', this, v) !== false){
24732                 var d = (this.doc.body || this.doc.documentElement);
24733                 d.innerHTML = v;
24734                 this.cleanUpPaste();
24735                 this.el.dom.value = d.innerHTML;
24736                 this.owner.fireEvent('push', this, v);
24737             }
24738         }
24739     },
24740
24741     // private
24742     deferFocus : function(){
24743         this.focus.defer(10, this);
24744     },
24745
24746     // doc'ed in Field
24747     focus : function(){
24748         if(this.win && !this.sourceEditMode){
24749             this.win.focus();
24750         }else{
24751             this.el.focus();
24752         }
24753     },
24754     
24755     assignDocWin: function()
24756     {
24757         var iframe = this.iframe;
24758         
24759          if(Roo.isIE){
24760             this.doc = iframe.contentWindow.document;
24761             this.win = iframe.contentWindow;
24762         } else {
24763 //            if (!Roo.get(this.frameId)) {
24764 //                return;
24765 //            }
24766 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24767 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24768             
24769             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24770                 return;
24771             }
24772             
24773             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24774             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24775         }
24776     },
24777     
24778     // private
24779     initEditor : function(){
24780         //console.log("INIT EDITOR");
24781         this.assignDocWin();
24782         
24783         
24784         
24785         this.doc.designMode="on";
24786         this.doc.open();
24787         this.doc.write(this.getDocMarkup());
24788         this.doc.close();
24789         
24790         var dbody = (this.doc.body || this.doc.documentElement);
24791         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24792         // this copies styles from the containing element into thsi one..
24793         // not sure why we need all of this..
24794         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24795         
24796         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24797         //ss['background-attachment'] = 'fixed'; // w3c
24798         dbody.bgProperties = 'fixed'; // ie
24799         //Roo.DomHelper.applyStyles(dbody, ss);
24800         Roo.EventManager.on(this.doc, {
24801             //'mousedown': this.onEditorEvent,
24802             'mouseup': this.onEditorEvent,
24803             'dblclick': this.onEditorEvent,
24804             'click': this.onEditorEvent,
24805             'keyup': this.onEditorEvent,
24806             buffer:100,
24807             scope: this
24808         });
24809         if(Roo.isGecko){
24810             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24811         }
24812         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24813             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24814         }
24815         this.initialized = true;
24816
24817         this.owner.fireEvent('initialize', this);
24818         this.pushValue();
24819     },
24820
24821     // private
24822     onDestroy : function(){
24823         
24824         
24825         
24826         if(this.rendered){
24827             
24828             //for (var i =0; i < this.toolbars.length;i++) {
24829             //    // fixme - ask toolbars for heights?
24830             //    this.toolbars[i].onDestroy();
24831            // }
24832             
24833             //this.wrap.dom.innerHTML = '';
24834             //this.wrap.remove();
24835         }
24836     },
24837
24838     // private
24839     onFirstFocus : function(){
24840         
24841         this.assignDocWin();
24842         
24843         
24844         this.activated = true;
24845          
24846     
24847         if(Roo.isGecko){ // prevent silly gecko errors
24848             this.win.focus();
24849             var s = this.win.getSelection();
24850             if(!s.focusNode || s.focusNode.nodeType != 3){
24851                 var r = s.getRangeAt(0);
24852                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24853                 r.collapse(true);
24854                 this.deferFocus();
24855             }
24856             try{
24857                 this.execCmd('useCSS', true);
24858                 this.execCmd('styleWithCSS', false);
24859             }catch(e){}
24860         }
24861         this.owner.fireEvent('activate', this);
24862     },
24863
24864     // private
24865     adjustFont: function(btn){
24866         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24867         //if(Roo.isSafari){ // safari
24868         //    adjust *= 2;
24869        // }
24870         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24871         if(Roo.isSafari){ // safari
24872             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24873             v =  (v < 10) ? 10 : v;
24874             v =  (v > 48) ? 48 : v;
24875             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24876             
24877         }
24878         
24879         
24880         v = Math.max(1, v+adjust);
24881         
24882         this.execCmd('FontSize', v  );
24883     },
24884
24885     onEditorEvent : function(e)
24886     {
24887         this.owner.fireEvent('editorevent', this, e);
24888       //  this.updateToolbar();
24889         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24890     },
24891
24892     insertTag : function(tg)
24893     {
24894         // could be a bit smarter... -> wrap the current selected tRoo..
24895         if (tg.toLowerCase() == 'span' ||
24896             tg.toLowerCase() == 'code' ||
24897             tg.toLowerCase() == 'sup' ||
24898             tg.toLowerCase() == 'sub' 
24899             ) {
24900             
24901             range = this.createRange(this.getSelection());
24902             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24903             wrappingNode.appendChild(range.extractContents());
24904             range.insertNode(wrappingNode);
24905
24906             return;
24907             
24908             
24909             
24910         }
24911         this.execCmd("formatblock",   tg);
24912         
24913     },
24914     
24915     insertText : function(txt)
24916     {
24917         
24918         
24919         var range = this.createRange();
24920         range.deleteContents();
24921                //alert(Sender.getAttribute('label'));
24922                
24923         range.insertNode(this.doc.createTextNode(txt));
24924     } ,
24925     
24926      
24927
24928     /**
24929      * Executes a Midas editor command on the editor document and performs necessary focus and
24930      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24931      * @param {String} cmd The Midas command
24932      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24933      */
24934     relayCmd : function(cmd, value){
24935         this.win.focus();
24936         this.execCmd(cmd, value);
24937         this.owner.fireEvent('editorevent', this);
24938         //this.updateToolbar();
24939         this.owner.deferFocus();
24940     },
24941
24942     /**
24943      * Executes a Midas editor command directly on the editor document.
24944      * For visual commands, you should use {@link #relayCmd} instead.
24945      * <b>This should only be called after the editor is initialized.</b>
24946      * @param {String} cmd The Midas command
24947      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24948      */
24949     execCmd : function(cmd, value){
24950         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24951         this.syncValue();
24952     },
24953  
24954  
24955    
24956     /**
24957      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24958      * to insert tRoo.
24959      * @param {String} text | dom node.. 
24960      */
24961     insertAtCursor : function(text)
24962     {
24963         
24964         if(!this.activated){
24965             return;
24966         }
24967         /*
24968         if(Roo.isIE){
24969             this.win.focus();
24970             var r = this.doc.selection.createRange();
24971             if(r){
24972                 r.collapse(true);
24973                 r.pasteHTML(text);
24974                 this.syncValue();
24975                 this.deferFocus();
24976             
24977             }
24978             return;
24979         }
24980         */
24981         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24982             this.win.focus();
24983             
24984             
24985             // from jquery ui (MIT licenced)
24986             var range, node;
24987             var win = this.win;
24988             
24989             if (win.getSelection && win.getSelection().getRangeAt) {
24990                 range = win.getSelection().getRangeAt(0);
24991                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24992                 range.insertNode(node);
24993             } else if (win.document.selection && win.document.selection.createRange) {
24994                 // no firefox support
24995                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24996                 win.document.selection.createRange().pasteHTML(txt);
24997             } else {
24998                 // no firefox support
24999                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25000                 this.execCmd('InsertHTML', txt);
25001             } 
25002             
25003             this.syncValue();
25004             
25005             this.deferFocus();
25006         }
25007     },
25008  // private
25009     mozKeyPress : function(e){
25010         if(e.ctrlKey){
25011             var c = e.getCharCode(), cmd;
25012           
25013             if(c > 0){
25014                 c = String.fromCharCode(c).toLowerCase();
25015                 switch(c){
25016                     case 'b':
25017                         cmd = 'bold';
25018                         break;
25019                     case 'i':
25020                         cmd = 'italic';
25021                         break;
25022                     
25023                     case 'u':
25024                         cmd = 'underline';
25025                         break;
25026                     
25027                     case 'v':
25028                         this.cleanUpPaste.defer(100, this);
25029                         return;
25030                         
25031                 }
25032                 if(cmd){
25033                     this.win.focus();
25034                     this.execCmd(cmd);
25035                     this.deferFocus();
25036                     e.preventDefault();
25037                 }
25038                 
25039             }
25040         }
25041     },
25042
25043     // private
25044     fixKeys : function(){ // load time branching for fastest keydown performance
25045         if(Roo.isIE){
25046             return function(e){
25047                 var k = e.getKey(), r;
25048                 if(k == e.TAB){
25049                     e.stopEvent();
25050                     r = this.doc.selection.createRange();
25051                     if(r){
25052                         r.collapse(true);
25053                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25054                         this.deferFocus();
25055                     }
25056                     return;
25057                 }
25058                 
25059                 if(k == e.ENTER){
25060                     r = this.doc.selection.createRange();
25061                     if(r){
25062                         var target = r.parentElement();
25063                         if(!target || target.tagName.toLowerCase() != 'li'){
25064                             e.stopEvent();
25065                             r.pasteHTML('<br />');
25066                             r.collapse(false);
25067                             r.select();
25068                         }
25069                     }
25070                 }
25071                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25072                     this.cleanUpPaste.defer(100, this);
25073                     return;
25074                 }
25075                 
25076                 
25077             };
25078         }else if(Roo.isOpera){
25079             return function(e){
25080                 var k = e.getKey();
25081                 if(k == e.TAB){
25082                     e.stopEvent();
25083                     this.win.focus();
25084                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25085                     this.deferFocus();
25086                 }
25087                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25088                     this.cleanUpPaste.defer(100, this);
25089                     return;
25090                 }
25091                 
25092             };
25093         }else if(Roo.isSafari){
25094             return function(e){
25095                 var k = e.getKey();
25096                 
25097                 if(k == e.TAB){
25098                     e.stopEvent();
25099                     this.execCmd('InsertText','\t');
25100                     this.deferFocus();
25101                     return;
25102                 }
25103                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25104                     this.cleanUpPaste.defer(100, this);
25105                     return;
25106                 }
25107                 
25108              };
25109         }
25110     }(),
25111     
25112     getAllAncestors: function()
25113     {
25114         var p = this.getSelectedNode();
25115         var a = [];
25116         if (!p) {
25117             a.push(p); // push blank onto stack..
25118             p = this.getParentElement();
25119         }
25120         
25121         
25122         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25123             a.push(p);
25124             p = p.parentNode;
25125         }
25126         a.push(this.doc.body);
25127         return a;
25128     },
25129     lastSel : false,
25130     lastSelNode : false,
25131     
25132     
25133     getSelection : function() 
25134     {
25135         this.assignDocWin();
25136         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25137     },
25138     
25139     getSelectedNode: function() 
25140     {
25141         // this may only work on Gecko!!!
25142         
25143         // should we cache this!!!!
25144         
25145         
25146         
25147          
25148         var range = this.createRange(this.getSelection()).cloneRange();
25149         
25150         if (Roo.isIE) {
25151             var parent = range.parentElement();
25152             while (true) {
25153                 var testRange = range.duplicate();
25154                 testRange.moveToElementText(parent);
25155                 if (testRange.inRange(range)) {
25156                     break;
25157                 }
25158                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25159                     break;
25160                 }
25161                 parent = parent.parentElement;
25162             }
25163             return parent;
25164         }
25165         
25166         // is ancestor a text element.
25167         var ac =  range.commonAncestorContainer;
25168         if (ac.nodeType == 3) {
25169             ac = ac.parentNode;
25170         }
25171         
25172         var ar = ac.childNodes;
25173          
25174         var nodes = [];
25175         var other_nodes = [];
25176         var has_other_nodes = false;
25177         for (var i=0;i<ar.length;i++) {
25178             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25179                 continue;
25180             }
25181             // fullly contained node.
25182             
25183             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25184                 nodes.push(ar[i]);
25185                 continue;
25186             }
25187             
25188             // probably selected..
25189             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25190                 other_nodes.push(ar[i]);
25191                 continue;
25192             }
25193             // outer..
25194             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25195                 continue;
25196             }
25197             
25198             
25199             has_other_nodes = true;
25200         }
25201         if (!nodes.length && other_nodes.length) {
25202             nodes= other_nodes;
25203         }
25204         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25205             return false;
25206         }
25207         
25208         return nodes[0];
25209     },
25210     createRange: function(sel)
25211     {
25212         // this has strange effects when using with 
25213         // top toolbar - not sure if it's a great idea.
25214         //this.editor.contentWindow.focus();
25215         if (typeof sel != "undefined") {
25216             try {
25217                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25218             } catch(e) {
25219                 return this.doc.createRange();
25220             }
25221         } else {
25222             return this.doc.createRange();
25223         }
25224     },
25225     getParentElement: function()
25226     {
25227         
25228         this.assignDocWin();
25229         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25230         
25231         var range = this.createRange(sel);
25232          
25233         try {
25234             var p = range.commonAncestorContainer;
25235             while (p.nodeType == 3) { // text node
25236                 p = p.parentNode;
25237             }
25238             return p;
25239         } catch (e) {
25240             return null;
25241         }
25242     
25243     },
25244     /***
25245      *
25246      * Range intersection.. the hard stuff...
25247      *  '-1' = before
25248      *  '0' = hits..
25249      *  '1' = after.
25250      *         [ -- selected range --- ]
25251      *   [fail]                        [fail]
25252      *
25253      *    basically..
25254      *      if end is before start or  hits it. fail.
25255      *      if start is after end or hits it fail.
25256      *
25257      *   if either hits (but other is outside. - then it's not 
25258      *   
25259      *    
25260      **/
25261     
25262     
25263     // @see http://www.thismuchiknow.co.uk/?p=64.
25264     rangeIntersectsNode : function(range, node)
25265     {
25266         var nodeRange = node.ownerDocument.createRange();
25267         try {
25268             nodeRange.selectNode(node);
25269         } catch (e) {
25270             nodeRange.selectNodeContents(node);
25271         }
25272     
25273         var rangeStartRange = range.cloneRange();
25274         rangeStartRange.collapse(true);
25275     
25276         var rangeEndRange = range.cloneRange();
25277         rangeEndRange.collapse(false);
25278     
25279         var nodeStartRange = nodeRange.cloneRange();
25280         nodeStartRange.collapse(true);
25281     
25282         var nodeEndRange = nodeRange.cloneRange();
25283         nodeEndRange.collapse(false);
25284     
25285         return rangeStartRange.compareBoundaryPoints(
25286                  Range.START_TO_START, nodeEndRange) == -1 &&
25287                rangeEndRange.compareBoundaryPoints(
25288                  Range.START_TO_START, nodeStartRange) == 1;
25289         
25290          
25291     },
25292     rangeCompareNode : function(range, node)
25293     {
25294         var nodeRange = node.ownerDocument.createRange();
25295         try {
25296             nodeRange.selectNode(node);
25297         } catch (e) {
25298             nodeRange.selectNodeContents(node);
25299         }
25300         
25301         
25302         range.collapse(true);
25303     
25304         nodeRange.collapse(true);
25305      
25306         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25307         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25308          
25309         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25310         
25311         var nodeIsBefore   =  ss == 1;
25312         var nodeIsAfter    = ee == -1;
25313         
25314         if (nodeIsBefore && nodeIsAfter) {
25315             return 0; // outer
25316         }
25317         if (!nodeIsBefore && nodeIsAfter) {
25318             return 1; //right trailed.
25319         }
25320         
25321         if (nodeIsBefore && !nodeIsAfter) {
25322             return 2;  // left trailed.
25323         }
25324         // fully contined.
25325         return 3;
25326     },
25327
25328     // private? - in a new class?
25329     cleanUpPaste :  function()
25330     {
25331         // cleans up the whole document..
25332         Roo.log('cleanuppaste');
25333         
25334         this.cleanUpChildren(this.doc.body);
25335         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25336         if (clean != this.doc.body.innerHTML) {
25337             this.doc.body.innerHTML = clean;
25338         }
25339         
25340     },
25341     
25342     cleanWordChars : function(input) {// change the chars to hex code
25343         var he = Roo.HtmlEditorCore;
25344         
25345         var output = input;
25346         Roo.each(he.swapCodes, function(sw) { 
25347             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25348             
25349             output = output.replace(swapper, sw[1]);
25350         });
25351         
25352         return output;
25353     },
25354     
25355     
25356     cleanUpChildren : function (n)
25357     {
25358         if (!n.childNodes.length) {
25359             return;
25360         }
25361         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25362            this.cleanUpChild(n.childNodes[i]);
25363         }
25364     },
25365     
25366     
25367         
25368     
25369     cleanUpChild : function (node)
25370     {
25371         var ed = this;
25372         //console.log(node);
25373         if (node.nodeName == "#text") {
25374             // clean up silly Windows -- stuff?
25375             return; 
25376         }
25377         if (node.nodeName == "#comment") {
25378             node.parentNode.removeChild(node);
25379             // clean up silly Windows -- stuff?
25380             return; 
25381         }
25382         var lcname = node.tagName.toLowerCase();
25383         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25384         // whitelist of tags..
25385         
25386         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25387             // remove node.
25388             node.parentNode.removeChild(node);
25389             return;
25390             
25391         }
25392         
25393         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25394         
25395         // spans with no attributes - just remove them..
25396         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25397             remove_keep_children = true;
25398         }
25399         
25400         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25401         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25402         
25403         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25404         //    remove_keep_children = true;
25405         //}
25406         
25407         if (remove_keep_children) {
25408             this.cleanUpChildren(node);
25409             // inserts everything just before this node...
25410             while (node.childNodes.length) {
25411                 var cn = node.childNodes[0];
25412                 node.removeChild(cn);
25413                 node.parentNode.insertBefore(cn, node);
25414             }
25415             node.parentNode.removeChild(node);
25416             return;
25417         }
25418         
25419         if (!node.attributes || !node.attributes.length) {
25420             
25421           
25422             
25423             
25424             this.cleanUpChildren(node);
25425             return;
25426         }
25427         
25428         function cleanAttr(n,v)
25429         {
25430             
25431             if (v.match(/^\./) || v.match(/^\//)) {
25432                 return;
25433             }
25434             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25435                 return;
25436             }
25437             if (v.match(/^#/)) {
25438                 return;
25439             }
25440             if (v.match(/^\{/)) { // allow template editing.
25441                 return;
25442             }
25443 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25444             node.removeAttribute(n);
25445             
25446         }
25447         
25448         var cwhite = this.cwhite;
25449         var cblack = this.cblack;
25450             
25451         function cleanStyle(n,v)
25452         {
25453             if (v.match(/expression/)) { //XSS?? should we even bother..
25454                 node.removeAttribute(n);
25455                 return;
25456             }
25457             
25458             var parts = v.split(/;/);
25459             var clean = [];
25460             
25461             Roo.each(parts, function(p) {
25462                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25463                 if (!p.length) {
25464                     return true;
25465                 }
25466                 var l = p.split(':').shift().replace(/\s+/g,'');
25467                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25468                 
25469                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25470 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25471                     //node.removeAttribute(n);
25472                     return true;
25473                 }
25474                 //Roo.log()
25475                 // only allow 'c whitelisted system attributes'
25476                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25477 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25478                     //node.removeAttribute(n);
25479                     return true;
25480                 }
25481                 
25482                 
25483                  
25484                 
25485                 clean.push(p);
25486                 return true;
25487             });
25488             if (clean.length) { 
25489                 node.setAttribute(n, clean.join(';'));
25490             } else {
25491                 node.removeAttribute(n);
25492             }
25493             
25494         }
25495         
25496         
25497         for (var i = node.attributes.length-1; i > -1 ; i--) {
25498             var a = node.attributes[i];
25499             //console.log(a);
25500             
25501             if (a.name.toLowerCase().substr(0,2)=='on')  {
25502                 node.removeAttribute(a.name);
25503                 continue;
25504             }
25505             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25506                 node.removeAttribute(a.name);
25507                 continue;
25508             }
25509             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25510                 cleanAttr(a.name,a.value); // fixme..
25511                 continue;
25512             }
25513             if (a.name == 'style') {
25514                 cleanStyle(a.name,a.value);
25515                 continue;
25516             }
25517             /// clean up MS crap..
25518             // tecnically this should be a list of valid class'es..
25519             
25520             
25521             if (a.name == 'class') {
25522                 if (a.value.match(/^Mso/)) {
25523                     node.removeAttribute('class');
25524                 }
25525                 
25526                 if (a.value.match(/^body$/)) {
25527                     node.removeAttribute('class');
25528                 }
25529                 continue;
25530             }
25531             
25532             // style cleanup!?
25533             // class cleanup?
25534             
25535         }
25536         
25537         
25538         this.cleanUpChildren(node);
25539         
25540         
25541     },
25542     
25543     /**
25544      * Clean up MS wordisms...
25545      */
25546     cleanWord : function(node)
25547     {
25548         if (!node) {
25549             this.cleanWord(this.doc.body);
25550             return;
25551         }
25552         
25553         if(
25554                 node.nodeName == 'SPAN' &&
25555                 !node.hasAttributes() &&
25556                 node.childNodes.length == 1 &&
25557                 node.firstChild.nodeName == "#text"  
25558         ) {
25559             var textNode = node.firstChild;
25560             node.removeChild(textNode);
25561             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25562                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25563             }
25564             node.parentNode.insertBefore(textNode, node);
25565             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25566                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25567             }
25568             node.parentNode.removeChild(node);
25569         }
25570         
25571         if (node.nodeName == "#text") {
25572             // clean up silly Windows -- stuff?
25573             return; 
25574         }
25575         if (node.nodeName == "#comment") {
25576             node.parentNode.removeChild(node);
25577             // clean up silly Windows -- stuff?
25578             return; 
25579         }
25580         
25581         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25582             node.parentNode.removeChild(node);
25583             return;
25584         }
25585         //Roo.log(node.tagName);
25586         // remove - but keep children..
25587         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25588             //Roo.log('-- removed');
25589             while (node.childNodes.length) {
25590                 var cn = node.childNodes[0];
25591                 node.removeChild(cn);
25592                 node.parentNode.insertBefore(cn, node);
25593                 // move node to parent - and clean it..
25594                 this.cleanWord(cn);
25595             }
25596             node.parentNode.removeChild(node);
25597             /// no need to iterate chidlren = it's got none..
25598             //this.iterateChildren(node, this.cleanWord);
25599             return;
25600         }
25601         // clean styles
25602         if (node.className.length) {
25603             
25604             var cn = node.className.split(/\W+/);
25605             var cna = [];
25606             Roo.each(cn, function(cls) {
25607                 if (cls.match(/Mso[a-zA-Z]+/)) {
25608                     return;
25609                 }
25610                 cna.push(cls);
25611             });
25612             node.className = cna.length ? cna.join(' ') : '';
25613             if (!cna.length) {
25614                 node.removeAttribute("class");
25615             }
25616         }
25617         
25618         if (node.hasAttribute("lang")) {
25619             node.removeAttribute("lang");
25620         }
25621         
25622         if (node.hasAttribute("style")) {
25623             
25624             var styles = node.getAttribute("style").split(";");
25625             var nstyle = [];
25626             Roo.each(styles, function(s) {
25627                 if (!s.match(/:/)) {
25628                     return;
25629                 }
25630                 var kv = s.split(":");
25631                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25632                     return;
25633                 }
25634                 // what ever is left... we allow.
25635                 nstyle.push(s);
25636             });
25637             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25638             if (!nstyle.length) {
25639                 node.removeAttribute('style');
25640             }
25641         }
25642         this.iterateChildren(node, this.cleanWord);
25643         
25644         
25645         
25646     },
25647     /**
25648      * iterateChildren of a Node, calling fn each time, using this as the scole..
25649      * @param {DomNode} node node to iterate children of.
25650      * @param {Function} fn method of this class to call on each item.
25651      */
25652     iterateChildren : function(node, fn)
25653     {
25654         if (!node.childNodes.length) {
25655                 return;
25656         }
25657         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25658            fn.call(this, node.childNodes[i])
25659         }
25660     },
25661     
25662     
25663     /**
25664      * cleanTableWidths.
25665      *
25666      * Quite often pasting from word etc.. results in tables with column and widths.
25667      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25668      *
25669      */
25670     cleanTableWidths : function(node)
25671     {
25672          
25673          
25674         if (!node) {
25675             this.cleanTableWidths(this.doc.body);
25676             return;
25677         }
25678         
25679         // ignore list...
25680         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25681             return; 
25682         }
25683         Roo.log(node.tagName);
25684         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25685             this.iterateChildren(node, this.cleanTableWidths);
25686             return;
25687         }
25688         if (node.hasAttribute('width')) {
25689             node.removeAttribute('width');
25690         }
25691         
25692          
25693         if (node.hasAttribute("style")) {
25694             // pretty basic...
25695             
25696             var styles = node.getAttribute("style").split(";");
25697             var nstyle = [];
25698             Roo.each(styles, function(s) {
25699                 if (!s.match(/:/)) {
25700                     return;
25701                 }
25702                 var kv = s.split(":");
25703                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25704                     return;
25705                 }
25706                 // what ever is left... we allow.
25707                 nstyle.push(s);
25708             });
25709             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25710             if (!nstyle.length) {
25711                 node.removeAttribute('style');
25712             }
25713         }
25714         
25715         this.iterateChildren(node, this.cleanTableWidths);
25716         
25717         
25718     },
25719     
25720     
25721     
25722     
25723     domToHTML : function(currentElement, depth, nopadtext) {
25724         
25725         depth = depth || 0;
25726         nopadtext = nopadtext || false;
25727     
25728         if (!currentElement) {
25729             return this.domToHTML(this.doc.body);
25730         }
25731         
25732         //Roo.log(currentElement);
25733         var j;
25734         var allText = false;
25735         var nodeName = currentElement.nodeName;
25736         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25737         
25738         if  (nodeName == '#text') {
25739             
25740             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25741         }
25742         
25743         
25744         var ret = '';
25745         if (nodeName != 'BODY') {
25746              
25747             var i = 0;
25748             // Prints the node tagName, such as <A>, <IMG>, etc
25749             if (tagName) {
25750                 var attr = [];
25751                 for(i = 0; i < currentElement.attributes.length;i++) {
25752                     // quoting?
25753                     var aname = currentElement.attributes.item(i).name;
25754                     if (!currentElement.attributes.item(i).value.length) {
25755                         continue;
25756                     }
25757                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25758                 }
25759                 
25760                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25761             } 
25762             else {
25763                 
25764                 // eack
25765             }
25766         } else {
25767             tagName = false;
25768         }
25769         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25770             return ret;
25771         }
25772         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25773             nopadtext = true;
25774         }
25775         
25776         
25777         // Traverse the tree
25778         i = 0;
25779         var currentElementChild = currentElement.childNodes.item(i);
25780         var allText = true;
25781         var innerHTML  = '';
25782         lastnode = '';
25783         while (currentElementChild) {
25784             // Formatting code (indent the tree so it looks nice on the screen)
25785             var nopad = nopadtext;
25786             if (lastnode == 'SPAN') {
25787                 nopad  = true;
25788             }
25789             // text
25790             if  (currentElementChild.nodeName == '#text') {
25791                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25792                 toadd = nopadtext ? toadd : toadd.trim();
25793                 if (!nopad && toadd.length > 80) {
25794                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25795                 }
25796                 innerHTML  += toadd;
25797                 
25798                 i++;
25799                 currentElementChild = currentElement.childNodes.item(i);
25800                 lastNode = '';
25801                 continue;
25802             }
25803             allText = false;
25804             
25805             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25806                 
25807             // Recursively traverse the tree structure of the child node
25808             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25809             lastnode = currentElementChild.nodeName;
25810             i++;
25811             currentElementChild=currentElement.childNodes.item(i);
25812         }
25813         
25814         ret += innerHTML;
25815         
25816         if (!allText) {
25817                 // The remaining code is mostly for formatting the tree
25818             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25819         }
25820         
25821         
25822         if (tagName) {
25823             ret+= "</"+tagName+">";
25824         }
25825         return ret;
25826         
25827     },
25828         
25829     applyBlacklists : function()
25830     {
25831         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25832         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25833         
25834         this.white = [];
25835         this.black = [];
25836         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25837             if (b.indexOf(tag) > -1) {
25838                 return;
25839             }
25840             this.white.push(tag);
25841             
25842         }, this);
25843         
25844         Roo.each(w, function(tag) {
25845             if (b.indexOf(tag) > -1) {
25846                 return;
25847             }
25848             if (this.white.indexOf(tag) > -1) {
25849                 return;
25850             }
25851             this.white.push(tag);
25852             
25853         }, this);
25854         
25855         
25856         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25857             if (w.indexOf(tag) > -1) {
25858                 return;
25859             }
25860             this.black.push(tag);
25861             
25862         }, this);
25863         
25864         Roo.each(b, function(tag) {
25865             if (w.indexOf(tag) > -1) {
25866                 return;
25867             }
25868             if (this.black.indexOf(tag) > -1) {
25869                 return;
25870             }
25871             this.black.push(tag);
25872             
25873         }, this);
25874         
25875         
25876         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25877         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25878         
25879         this.cwhite = [];
25880         this.cblack = [];
25881         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25882             if (b.indexOf(tag) > -1) {
25883                 return;
25884             }
25885             this.cwhite.push(tag);
25886             
25887         }, this);
25888         
25889         Roo.each(w, function(tag) {
25890             if (b.indexOf(tag) > -1) {
25891                 return;
25892             }
25893             if (this.cwhite.indexOf(tag) > -1) {
25894                 return;
25895             }
25896             this.cwhite.push(tag);
25897             
25898         }, this);
25899         
25900         
25901         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25902             if (w.indexOf(tag) > -1) {
25903                 return;
25904             }
25905             this.cblack.push(tag);
25906             
25907         }, this);
25908         
25909         Roo.each(b, function(tag) {
25910             if (w.indexOf(tag) > -1) {
25911                 return;
25912             }
25913             if (this.cblack.indexOf(tag) > -1) {
25914                 return;
25915             }
25916             this.cblack.push(tag);
25917             
25918         }, this);
25919     },
25920     
25921     setStylesheets : function(stylesheets)
25922     {
25923         if(typeof(stylesheets) == 'string'){
25924             Roo.get(this.iframe.contentDocument.head).createChild({
25925                 tag : 'link',
25926                 rel : 'stylesheet',
25927                 type : 'text/css',
25928                 href : stylesheets
25929             });
25930             
25931             return;
25932         }
25933         var _this = this;
25934      
25935         Roo.each(stylesheets, function(s) {
25936             if(!s.length){
25937                 return;
25938             }
25939             
25940             Roo.get(_this.iframe.contentDocument.head).createChild({
25941                 tag : 'link',
25942                 rel : 'stylesheet',
25943                 type : 'text/css',
25944                 href : s
25945             });
25946         });
25947
25948         
25949     },
25950     
25951     removeStylesheets : function()
25952     {
25953         var _this = this;
25954         
25955         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25956             s.remove();
25957         });
25958     },
25959     
25960     setStyle : function(style)
25961     {
25962         Roo.get(this.iframe.contentDocument.head).createChild({
25963             tag : 'style',
25964             type : 'text/css',
25965             html : style
25966         });
25967
25968         return;
25969     }
25970     
25971     // hide stuff that is not compatible
25972     /**
25973      * @event blur
25974      * @hide
25975      */
25976     /**
25977      * @event change
25978      * @hide
25979      */
25980     /**
25981      * @event focus
25982      * @hide
25983      */
25984     /**
25985      * @event specialkey
25986      * @hide
25987      */
25988     /**
25989      * @cfg {String} fieldClass @hide
25990      */
25991     /**
25992      * @cfg {String} focusClass @hide
25993      */
25994     /**
25995      * @cfg {String} autoCreate @hide
25996      */
25997     /**
25998      * @cfg {String} inputType @hide
25999      */
26000     /**
26001      * @cfg {String} invalidClass @hide
26002      */
26003     /**
26004      * @cfg {String} invalidText @hide
26005      */
26006     /**
26007      * @cfg {String} msgFx @hide
26008      */
26009     /**
26010      * @cfg {String} validateOnBlur @hide
26011      */
26012 });
26013
26014 Roo.HtmlEditorCore.white = [
26015         'area', 'br', 'img', 'input', 'hr', 'wbr',
26016         
26017        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26018        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26019        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26020        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26021        'table',   'ul',         'xmp', 
26022        
26023        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26024       'thead',   'tr', 
26025      
26026       'dir', 'menu', 'ol', 'ul', 'dl',
26027        
26028       'embed',  'object'
26029 ];
26030
26031
26032 Roo.HtmlEditorCore.black = [
26033     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26034         'applet', // 
26035         'base',   'basefont', 'bgsound', 'blink',  'body', 
26036         'frame',  'frameset', 'head',    'html',   'ilayer', 
26037         'iframe', 'layer',  'link',     'meta',    'object',   
26038         'script', 'style' ,'title',  'xml' // clean later..
26039 ];
26040 Roo.HtmlEditorCore.clean = [
26041     'script', 'style', 'title', 'xml'
26042 ];
26043 Roo.HtmlEditorCore.remove = [
26044     'font'
26045 ];
26046 // attributes..
26047
26048 Roo.HtmlEditorCore.ablack = [
26049     'on'
26050 ];
26051     
26052 Roo.HtmlEditorCore.aclean = [ 
26053     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26054 ];
26055
26056 // protocols..
26057 Roo.HtmlEditorCore.pwhite= [
26058         'http',  'https',  'mailto'
26059 ];
26060
26061 // white listed style attributes.
26062 Roo.HtmlEditorCore.cwhite= [
26063       //  'text-align', /// default is to allow most things..
26064       
26065          
26066 //        'font-size'//??
26067 ];
26068
26069 // black listed style attributes.
26070 Roo.HtmlEditorCore.cblack= [
26071       //  'font-size' -- this can be set by the project 
26072 ];
26073
26074
26075 Roo.HtmlEditorCore.swapCodes   =[ 
26076     [    8211, "&#8211;" ], 
26077     [    8212, "&#8212;" ], 
26078     [    8216,  "'" ],  
26079     [    8217, "'" ],  
26080     [    8220, '"' ],  
26081     [    8221, '"' ],  
26082     [    8226, "*" ],  
26083     [    8230, "..." ]
26084 ]; 
26085
26086     /*
26087  * - LGPL
26088  *
26089  * HtmlEditor
26090  * 
26091  */
26092
26093 /**
26094  * @class Roo.bootstrap.HtmlEditor
26095  * @extends Roo.bootstrap.TextArea
26096  * Bootstrap HtmlEditor class
26097
26098  * @constructor
26099  * Create a new HtmlEditor
26100  * @param {Object} config The config object
26101  */
26102
26103 Roo.bootstrap.HtmlEditor = function(config){
26104     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26105     if (!this.toolbars) {
26106         this.toolbars = [];
26107     }
26108     
26109     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26110     this.addEvents({
26111             /**
26112              * @event initialize
26113              * Fires when the editor is fully initialized (including the iframe)
26114              * @param {HtmlEditor} this
26115              */
26116             initialize: true,
26117             /**
26118              * @event activate
26119              * Fires when the editor is first receives the focus. Any insertion must wait
26120              * until after this event.
26121              * @param {HtmlEditor} this
26122              */
26123             activate: true,
26124              /**
26125              * @event beforesync
26126              * Fires before the textarea is updated with content from the editor iframe. Return false
26127              * to cancel the sync.
26128              * @param {HtmlEditor} this
26129              * @param {String} html
26130              */
26131             beforesync: true,
26132              /**
26133              * @event beforepush
26134              * Fires before the iframe editor is updated with content from the textarea. Return false
26135              * to cancel the push.
26136              * @param {HtmlEditor} this
26137              * @param {String} html
26138              */
26139             beforepush: true,
26140              /**
26141              * @event sync
26142              * Fires when the textarea is updated with content from the editor iframe.
26143              * @param {HtmlEditor} this
26144              * @param {String} html
26145              */
26146             sync: true,
26147              /**
26148              * @event push
26149              * Fires when the iframe editor is updated with content from the textarea.
26150              * @param {HtmlEditor} this
26151              * @param {String} html
26152              */
26153             push: true,
26154              /**
26155              * @event editmodechange
26156              * Fires when the editor switches edit modes
26157              * @param {HtmlEditor} this
26158              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26159              */
26160             editmodechange: true,
26161             /**
26162              * @event editorevent
26163              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26164              * @param {HtmlEditor} this
26165              */
26166             editorevent: true,
26167             /**
26168              * @event firstfocus
26169              * Fires when on first focus - needed by toolbars..
26170              * @param {HtmlEditor} this
26171              */
26172             firstfocus: true,
26173             /**
26174              * @event autosave
26175              * Auto save the htmlEditor value as a file into Events
26176              * @param {HtmlEditor} this
26177              */
26178             autosave: true,
26179             /**
26180              * @event savedpreview
26181              * preview the saved version of htmlEditor
26182              * @param {HtmlEditor} this
26183              */
26184             savedpreview: true
26185         });
26186 };
26187
26188
26189 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26190     
26191     
26192       /**
26193      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26194      */
26195     toolbars : false,
26196     
26197      /**
26198     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26199     */
26200     btns : [],
26201    
26202      /**
26203      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26204      *                        Roo.resizable.
26205      */
26206     resizable : false,
26207      /**
26208      * @cfg {Number} height (in pixels)
26209      */   
26210     height: 300,
26211    /**
26212      * @cfg {Number} width (in pixels)
26213      */   
26214     width: false,
26215     
26216     /**
26217      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26218      * 
26219      */
26220     stylesheets: false,
26221     
26222     // id of frame..
26223     frameId: false,
26224     
26225     // private properties
26226     validationEvent : false,
26227     deferHeight: true,
26228     initialized : false,
26229     activated : false,
26230     
26231     onFocus : Roo.emptyFn,
26232     iframePad:3,
26233     hideMode:'offsets',
26234     
26235     tbContainer : false,
26236     
26237     bodyCls : '',
26238     
26239     toolbarContainer :function() {
26240         return this.wrap.select('.x-html-editor-tb',true).first();
26241     },
26242
26243     /**
26244      * Protected method that will not generally be called directly. It
26245      * is called when the editor creates its toolbar. Override this method if you need to
26246      * add custom toolbar buttons.
26247      * @param {HtmlEditor} editor
26248      */
26249     createToolbar : function(){
26250         Roo.log('renewing');
26251         Roo.log("create toolbars");
26252         
26253         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26254         this.toolbars[0].render(this.toolbarContainer());
26255         
26256         return;
26257         
26258 //        if (!editor.toolbars || !editor.toolbars.length) {
26259 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26260 //        }
26261 //        
26262 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26263 //            editor.toolbars[i] = Roo.factory(
26264 //                    typeof(editor.toolbars[i]) == 'string' ?
26265 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26266 //                Roo.bootstrap.HtmlEditor);
26267 //            editor.toolbars[i].init(editor);
26268 //        }
26269     },
26270
26271      
26272     // private
26273     onRender : function(ct, position)
26274     {
26275        // Roo.log("Call onRender: " + this.xtype);
26276         var _t = this;
26277         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26278       
26279         this.wrap = this.inputEl().wrap({
26280             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26281         });
26282         
26283         this.editorcore.onRender(ct, position);
26284          
26285         if (this.resizable) {
26286             this.resizeEl = new Roo.Resizable(this.wrap, {
26287                 pinned : true,
26288                 wrap: true,
26289                 dynamic : true,
26290                 minHeight : this.height,
26291                 height: this.height,
26292                 handles : this.resizable,
26293                 width: this.width,
26294                 listeners : {
26295                     resize : function(r, w, h) {
26296                         _t.onResize(w,h); // -something
26297                     }
26298                 }
26299             });
26300             
26301         }
26302         this.createToolbar(this);
26303        
26304         
26305         if(!this.width && this.resizable){
26306             this.setSize(this.wrap.getSize());
26307         }
26308         if (this.resizeEl) {
26309             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26310             // should trigger onReize..
26311         }
26312         
26313     },
26314
26315     // private
26316     onResize : function(w, h)
26317     {
26318         Roo.log('resize: ' +w + ',' + h );
26319         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26320         var ew = false;
26321         var eh = false;
26322         
26323         if(this.inputEl() ){
26324             if(typeof w == 'number'){
26325                 var aw = w - this.wrap.getFrameWidth('lr');
26326                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26327                 ew = aw;
26328             }
26329             if(typeof h == 'number'){
26330                  var tbh = -11;  // fixme it needs to tool bar size!
26331                 for (var i =0; i < this.toolbars.length;i++) {
26332                     // fixme - ask toolbars for heights?
26333                     tbh += this.toolbars[i].el.getHeight();
26334                     //if (this.toolbars[i].footer) {
26335                     //    tbh += this.toolbars[i].footer.el.getHeight();
26336                     //}
26337                 }
26338               
26339                 
26340                 
26341                 
26342                 
26343                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26344                 ah -= 5; // knock a few pixes off for look..
26345                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26346                 var eh = ah;
26347             }
26348         }
26349         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26350         this.editorcore.onResize(ew,eh);
26351         
26352     },
26353
26354     /**
26355      * Toggles the editor between standard and source edit mode.
26356      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26357      */
26358     toggleSourceEdit : function(sourceEditMode)
26359     {
26360         this.editorcore.toggleSourceEdit(sourceEditMode);
26361         
26362         if(this.editorcore.sourceEditMode){
26363             Roo.log('editor - showing textarea');
26364             
26365 //            Roo.log('in');
26366 //            Roo.log(this.syncValue());
26367             this.syncValue();
26368             this.inputEl().removeClass(['hide', 'x-hidden']);
26369             this.inputEl().dom.removeAttribute('tabIndex');
26370             this.inputEl().focus();
26371         }else{
26372             Roo.log('editor - hiding textarea');
26373 //            Roo.log('out')
26374 //            Roo.log(this.pushValue()); 
26375             this.pushValue();
26376             
26377             this.inputEl().addClass(['hide', 'x-hidden']);
26378             this.inputEl().dom.setAttribute('tabIndex', -1);
26379             //this.deferFocus();
26380         }
26381          
26382         if(this.resizable){
26383             this.setSize(this.wrap.getSize());
26384         }
26385         
26386         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26387     },
26388  
26389     // private (for BoxComponent)
26390     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26391
26392     // private (for BoxComponent)
26393     getResizeEl : function(){
26394         return this.wrap;
26395     },
26396
26397     // private (for BoxComponent)
26398     getPositionEl : function(){
26399         return this.wrap;
26400     },
26401
26402     // private
26403     initEvents : function(){
26404         this.originalValue = this.getValue();
26405     },
26406
26407 //    /**
26408 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26409 //     * @method
26410 //     */
26411 //    markInvalid : Roo.emptyFn,
26412 //    /**
26413 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26414 //     * @method
26415 //     */
26416 //    clearInvalid : Roo.emptyFn,
26417
26418     setValue : function(v){
26419         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26420         this.editorcore.pushValue();
26421     },
26422
26423      
26424     // private
26425     deferFocus : function(){
26426         this.focus.defer(10, this);
26427     },
26428
26429     // doc'ed in Field
26430     focus : function(){
26431         this.editorcore.focus();
26432         
26433     },
26434       
26435
26436     // private
26437     onDestroy : function(){
26438         
26439         
26440         
26441         if(this.rendered){
26442             
26443             for (var i =0; i < this.toolbars.length;i++) {
26444                 // fixme - ask toolbars for heights?
26445                 this.toolbars[i].onDestroy();
26446             }
26447             
26448             this.wrap.dom.innerHTML = '';
26449             this.wrap.remove();
26450         }
26451     },
26452
26453     // private
26454     onFirstFocus : function(){
26455         //Roo.log("onFirstFocus");
26456         this.editorcore.onFirstFocus();
26457          for (var i =0; i < this.toolbars.length;i++) {
26458             this.toolbars[i].onFirstFocus();
26459         }
26460         
26461     },
26462     
26463     // private
26464     syncValue : function()
26465     {   
26466         this.editorcore.syncValue();
26467     },
26468     
26469     pushValue : function()
26470     {   
26471         this.editorcore.pushValue();
26472     }
26473      
26474     
26475     // hide stuff that is not compatible
26476     /**
26477      * @event blur
26478      * @hide
26479      */
26480     /**
26481      * @event change
26482      * @hide
26483      */
26484     /**
26485      * @event focus
26486      * @hide
26487      */
26488     /**
26489      * @event specialkey
26490      * @hide
26491      */
26492     /**
26493      * @cfg {String} fieldClass @hide
26494      */
26495     /**
26496      * @cfg {String} focusClass @hide
26497      */
26498     /**
26499      * @cfg {String} autoCreate @hide
26500      */
26501     /**
26502      * @cfg {String} inputType @hide
26503      */
26504      
26505     /**
26506      * @cfg {String} invalidText @hide
26507      */
26508     /**
26509      * @cfg {String} msgFx @hide
26510      */
26511     /**
26512      * @cfg {String} validateOnBlur @hide
26513      */
26514 });
26515  
26516     
26517    
26518    
26519    
26520       
26521 Roo.namespace('Roo.bootstrap.htmleditor');
26522 /**
26523  * @class Roo.bootstrap.HtmlEditorToolbar1
26524  * Basic Toolbar
26525  * 
26526  * @example
26527  * Usage:
26528  *
26529  new Roo.bootstrap.HtmlEditor({
26530     ....
26531     toolbars : [
26532         new Roo.bootstrap.HtmlEditorToolbar1({
26533             disable : { fonts: 1 , format: 1, ..., ... , ...],
26534             btns : [ .... ]
26535         })
26536     }
26537      
26538  * 
26539  * @cfg {Object} disable List of elements to disable..
26540  * @cfg {Array} btns List of additional buttons.
26541  * 
26542  * 
26543  * NEEDS Extra CSS? 
26544  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26545  */
26546  
26547 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26548 {
26549     
26550     Roo.apply(this, config);
26551     
26552     // default disabled, based on 'good practice'..
26553     this.disable = this.disable || {};
26554     Roo.applyIf(this.disable, {
26555         fontSize : true,
26556         colors : true,
26557         specialElements : true
26558     });
26559     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26560     
26561     this.editor = config.editor;
26562     this.editorcore = config.editor.editorcore;
26563     
26564     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26565     
26566     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26567     // dont call parent... till later.
26568 }
26569 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26570      
26571     bar : true,
26572     
26573     editor : false,
26574     editorcore : false,
26575     
26576     
26577     formats : [
26578         "p" ,  
26579         "h1","h2","h3","h4","h5","h6", 
26580         "pre", "code", 
26581         "abbr", "acronym", "address", "cite", "samp", "var",
26582         'div','span'
26583     ],
26584     
26585     onRender : function(ct, position)
26586     {
26587        // Roo.log("Call onRender: " + this.xtype);
26588         
26589        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26590        Roo.log(this.el);
26591        this.el.dom.style.marginBottom = '0';
26592        var _this = this;
26593        var editorcore = this.editorcore;
26594        var editor= this.editor;
26595        
26596        var children = [];
26597        var btn = function(id,cmd , toggle, handler, html){
26598        
26599             var  event = toggle ? 'toggle' : 'click';
26600        
26601             var a = {
26602                 size : 'sm',
26603                 xtype: 'Button',
26604                 xns: Roo.bootstrap,
26605                 //glyphicon : id,
26606                 fa: id,
26607                 cmd : id || cmd,
26608                 enableToggle:toggle !== false,
26609                 html : html || '',
26610                 pressed : toggle ? false : null,
26611                 listeners : {}
26612             };
26613             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26614                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26615             };
26616             children.push(a);
26617             return a;
26618        }
26619        
26620     //    var cb_box = function...
26621         
26622         var style = {
26623                 xtype: 'Button',
26624                 size : 'sm',
26625                 xns: Roo.bootstrap,
26626                 fa : 'font',
26627                 //html : 'submit'
26628                 menu : {
26629                     xtype: 'Menu',
26630                     xns: Roo.bootstrap,
26631                     items:  []
26632                 }
26633         };
26634         Roo.each(this.formats, function(f) {
26635             style.menu.items.push({
26636                 xtype :'MenuItem',
26637                 xns: Roo.bootstrap,
26638                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26639                 tagname : f,
26640                 listeners : {
26641                     click : function()
26642                     {
26643                         editorcore.insertTag(this.tagname);
26644                         editor.focus();
26645                     }
26646                 }
26647                 
26648             });
26649         });
26650         children.push(style);   
26651         
26652         btn('bold',false,true);
26653         btn('italic',false,true);
26654         btn('align-left', 'justifyleft',true);
26655         btn('align-center', 'justifycenter',true);
26656         btn('align-right' , 'justifyright',true);
26657         btn('link', false, false, function(btn) {
26658             //Roo.log("create link?");
26659             var url = prompt(this.createLinkText, this.defaultLinkValue);
26660             if(url && url != 'http:/'+'/'){
26661                 this.editorcore.relayCmd('createlink', url);
26662             }
26663         }),
26664         btn('list','insertunorderedlist',true);
26665         btn('pencil', false,true, function(btn){
26666                 Roo.log(this);
26667                 this.toggleSourceEdit(btn.pressed);
26668         });
26669         
26670         if (this.editor.btns.length > 0) {
26671             for (var i = 0; i<this.editor.btns.length; i++) {
26672                 children.push(this.editor.btns[i]);
26673             }
26674         }
26675         
26676         /*
26677         var cog = {
26678                 xtype: 'Button',
26679                 size : 'sm',
26680                 xns: Roo.bootstrap,
26681                 glyphicon : 'cog',
26682                 //html : 'submit'
26683                 menu : {
26684                     xtype: 'Menu',
26685                     xns: Roo.bootstrap,
26686                     items:  []
26687                 }
26688         };
26689         
26690         cog.menu.items.push({
26691             xtype :'MenuItem',
26692             xns: Roo.bootstrap,
26693             html : Clean styles,
26694             tagname : f,
26695             listeners : {
26696                 click : function()
26697                 {
26698                     editorcore.insertTag(this.tagname);
26699                     editor.focus();
26700                 }
26701             }
26702             
26703         });
26704        */
26705         
26706          
26707        this.xtype = 'NavSimplebar';
26708         
26709         for(var i=0;i< children.length;i++) {
26710             
26711             this.buttons.add(this.addxtypeChild(children[i]));
26712             
26713         }
26714         
26715         editor.on('editorevent', this.updateToolbar, this);
26716     },
26717     onBtnClick : function(id)
26718     {
26719        this.editorcore.relayCmd(id);
26720        this.editorcore.focus();
26721     },
26722     
26723     /**
26724      * Protected method that will not generally be called directly. It triggers
26725      * a toolbar update by reading the markup state of the current selection in the editor.
26726      */
26727     updateToolbar: function(){
26728
26729         if(!this.editorcore.activated){
26730             this.editor.onFirstFocus(); // is this neeed?
26731             return;
26732         }
26733
26734         var btns = this.buttons; 
26735         var doc = this.editorcore.doc;
26736         btns.get('bold').setActive(doc.queryCommandState('bold'));
26737         btns.get('italic').setActive(doc.queryCommandState('italic'));
26738         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26739         
26740         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26741         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26742         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26743         
26744         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26745         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26746          /*
26747         
26748         var ans = this.editorcore.getAllAncestors();
26749         if (this.formatCombo) {
26750             
26751             
26752             var store = this.formatCombo.store;
26753             this.formatCombo.setValue("");
26754             for (var i =0; i < ans.length;i++) {
26755                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26756                     // select it..
26757                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26758                     break;
26759                 }
26760             }
26761         }
26762         
26763         
26764         
26765         // hides menus... - so this cant be on a menu...
26766         Roo.bootstrap.MenuMgr.hideAll();
26767         */
26768         Roo.bootstrap.MenuMgr.hideAll();
26769         //this.editorsyncValue();
26770     },
26771     onFirstFocus: function() {
26772         this.buttons.each(function(item){
26773            item.enable();
26774         });
26775     },
26776     toggleSourceEdit : function(sourceEditMode){
26777         
26778           
26779         if(sourceEditMode){
26780             Roo.log("disabling buttons");
26781            this.buttons.each( function(item){
26782                 if(item.cmd != 'pencil'){
26783                     item.disable();
26784                 }
26785             });
26786           
26787         }else{
26788             Roo.log("enabling buttons");
26789             if(this.editorcore.initialized){
26790                 this.buttons.each( function(item){
26791                     item.enable();
26792                 });
26793             }
26794             
26795         }
26796         Roo.log("calling toggole on editor");
26797         // tell the editor that it's been pressed..
26798         this.editor.toggleSourceEdit(sourceEditMode);
26799        
26800     }
26801 });
26802
26803
26804
26805
26806  
26807 /*
26808  * - LGPL
26809  */
26810
26811 /**
26812  * @class Roo.bootstrap.Markdown
26813  * @extends Roo.bootstrap.TextArea
26814  * Bootstrap Showdown editable area
26815  * @cfg {string} content
26816  * 
26817  * @constructor
26818  * Create a new Showdown
26819  */
26820
26821 Roo.bootstrap.Markdown = function(config){
26822     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26823    
26824 };
26825
26826 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26827     
26828     editing :false,
26829     
26830     initEvents : function()
26831     {
26832         
26833         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26834         this.markdownEl = this.el.createChild({
26835             cls : 'roo-markdown-area'
26836         });
26837         this.inputEl().addClass('d-none');
26838         if (this.getValue() == '') {
26839             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26840             
26841         } else {
26842             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26843         }
26844         this.markdownEl.on('click', this.toggleTextEdit, this);
26845         this.on('blur', this.toggleTextEdit, this);
26846         this.on('specialkey', this.resizeTextArea, this);
26847     },
26848     
26849     toggleTextEdit : function()
26850     {
26851         var sh = this.markdownEl.getHeight();
26852         this.inputEl().addClass('d-none');
26853         this.markdownEl.addClass('d-none');
26854         if (!this.editing) {
26855             // show editor?
26856             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26857             this.inputEl().removeClass('d-none');
26858             this.inputEl().focus();
26859             this.editing = true;
26860             return;
26861         }
26862         // show showdown...
26863         this.updateMarkdown();
26864         this.markdownEl.removeClass('d-none');
26865         this.editing = false;
26866         return;
26867     },
26868     updateMarkdown : function()
26869     {
26870         if (this.getValue() == '') {
26871             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26872             return;
26873         }
26874  
26875         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26876     },
26877     
26878     resizeTextArea: function () {
26879         
26880         var sh = 100;
26881         Roo.log([sh, this.getValue().split("\n").length * 30]);
26882         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26883     },
26884     setValue : function(val)
26885     {
26886         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26887         if (!this.editing) {
26888             this.updateMarkdown();
26889         }
26890         
26891     },
26892     focus : function()
26893     {
26894         if (!this.editing) {
26895             this.toggleTextEdit();
26896         }
26897         
26898     }
26899
26900
26901 });
26902 /**
26903  * @class Roo.bootstrap.Table.AbstractSelectionModel
26904  * @extends Roo.util.Observable
26905  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26906  * implemented by descendant classes.  This class should not be directly instantiated.
26907  * @constructor
26908  */
26909 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26910     this.locked = false;
26911     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26912 };
26913
26914
26915 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26916     /** @ignore Called by the grid automatically. Do not call directly. */
26917     init : function(grid){
26918         this.grid = grid;
26919         this.initEvents();
26920     },
26921
26922     /**
26923      * Locks the selections.
26924      */
26925     lock : function(){
26926         this.locked = true;
26927     },
26928
26929     /**
26930      * Unlocks the selections.
26931      */
26932     unlock : function(){
26933         this.locked = false;
26934     },
26935
26936     /**
26937      * Returns true if the selections are locked.
26938      * @return {Boolean}
26939      */
26940     isLocked : function(){
26941         return this.locked;
26942     },
26943     
26944     
26945     initEvents : function ()
26946     {
26947         
26948     }
26949 });
26950 /**
26951  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26952  * @class Roo.bootstrap.Table.RowSelectionModel
26953  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26954  * It supports multiple selections and keyboard selection/navigation. 
26955  * @constructor
26956  * @param {Object} config
26957  */
26958
26959 Roo.bootstrap.Table.RowSelectionModel = function(config){
26960     Roo.apply(this, config);
26961     this.selections = new Roo.util.MixedCollection(false, function(o){
26962         return o.id;
26963     });
26964
26965     this.last = false;
26966     this.lastActive = false;
26967
26968     this.addEvents({
26969         /**
26970              * @event selectionchange
26971              * Fires when the selection changes
26972              * @param {SelectionModel} this
26973              */
26974             "selectionchange" : true,
26975         /**
26976              * @event afterselectionchange
26977              * Fires after the selection changes (eg. by key press or clicking)
26978              * @param {SelectionModel} this
26979              */
26980             "afterselectionchange" : true,
26981         /**
26982              * @event beforerowselect
26983              * Fires when a row is selected being selected, return false to cancel.
26984              * @param {SelectionModel} this
26985              * @param {Number} rowIndex The selected index
26986              * @param {Boolean} keepExisting False if other selections will be cleared
26987              */
26988             "beforerowselect" : true,
26989         /**
26990              * @event rowselect
26991              * Fires when a row is selected.
26992              * @param {SelectionModel} this
26993              * @param {Number} rowIndex The selected index
26994              * @param {Roo.data.Record} r The record
26995              */
26996             "rowselect" : true,
26997         /**
26998              * @event rowdeselect
26999              * Fires when a row is deselected.
27000              * @param {SelectionModel} this
27001              * @param {Number} rowIndex The selected index
27002              */
27003         "rowdeselect" : true
27004     });
27005     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27006     this.locked = false;
27007  };
27008
27009 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27010     /**
27011      * @cfg {Boolean} singleSelect
27012      * True to allow selection of only one row at a time (defaults to false)
27013      */
27014     singleSelect : false,
27015
27016     // private
27017     initEvents : function()
27018     {
27019
27020         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27021         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27022         //}else{ // allow click to work like normal
27023          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27024         //}
27025         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27026         this.grid.on("rowclick", this.handleMouseDown, this);
27027         
27028         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27029             "up" : function(e){
27030                 if(!e.shiftKey){
27031                     this.selectPrevious(e.shiftKey);
27032                 }else if(this.last !== false && this.lastActive !== false){
27033                     var last = this.last;
27034                     this.selectRange(this.last,  this.lastActive-1);
27035                     this.grid.getView().focusRow(this.lastActive);
27036                     if(last !== false){
27037                         this.last = last;
27038                     }
27039                 }else{
27040                     this.selectFirstRow();
27041                 }
27042                 this.fireEvent("afterselectionchange", this);
27043             },
27044             "down" : function(e){
27045                 if(!e.shiftKey){
27046                     this.selectNext(e.shiftKey);
27047                 }else if(this.last !== false && this.lastActive !== false){
27048                     var last = this.last;
27049                     this.selectRange(this.last,  this.lastActive+1);
27050                     this.grid.getView().focusRow(this.lastActive);
27051                     if(last !== false){
27052                         this.last = last;
27053                     }
27054                 }else{
27055                     this.selectFirstRow();
27056                 }
27057                 this.fireEvent("afterselectionchange", this);
27058             },
27059             scope: this
27060         });
27061         this.grid.store.on('load', function(){
27062             this.selections.clear();
27063         },this);
27064         /*
27065         var view = this.grid.view;
27066         view.on("refresh", this.onRefresh, this);
27067         view.on("rowupdated", this.onRowUpdated, this);
27068         view.on("rowremoved", this.onRemove, this);
27069         */
27070     },
27071
27072     // private
27073     onRefresh : function()
27074     {
27075         var ds = this.grid.store, i, v = this.grid.view;
27076         var s = this.selections;
27077         s.each(function(r){
27078             if((i = ds.indexOfId(r.id)) != -1){
27079                 v.onRowSelect(i);
27080             }else{
27081                 s.remove(r);
27082             }
27083         });
27084     },
27085
27086     // private
27087     onRemove : function(v, index, r){
27088         this.selections.remove(r);
27089     },
27090
27091     // private
27092     onRowUpdated : function(v, index, r){
27093         if(this.isSelected(r)){
27094             v.onRowSelect(index);
27095         }
27096     },
27097
27098     /**
27099      * Select records.
27100      * @param {Array} records The records to select
27101      * @param {Boolean} keepExisting (optional) True to keep existing selections
27102      */
27103     selectRecords : function(records, keepExisting)
27104     {
27105         if(!keepExisting){
27106             this.clearSelections();
27107         }
27108             var ds = this.grid.store;
27109         for(var i = 0, len = records.length; i < len; i++){
27110             this.selectRow(ds.indexOf(records[i]), true);
27111         }
27112     },
27113
27114     /**
27115      * Gets the number of selected rows.
27116      * @return {Number}
27117      */
27118     getCount : function(){
27119         return this.selections.length;
27120     },
27121
27122     /**
27123      * Selects the first row in the grid.
27124      */
27125     selectFirstRow : function(){
27126         this.selectRow(0);
27127     },
27128
27129     /**
27130      * Select the last row.
27131      * @param {Boolean} keepExisting (optional) True to keep existing selections
27132      */
27133     selectLastRow : function(keepExisting){
27134         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27135         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27136     },
27137
27138     /**
27139      * Selects the row immediately following the last selected row.
27140      * @param {Boolean} keepExisting (optional) True to keep existing selections
27141      */
27142     selectNext : function(keepExisting)
27143     {
27144             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27145             this.selectRow(this.last+1, keepExisting);
27146             this.grid.getView().focusRow(this.last);
27147         }
27148     },
27149
27150     /**
27151      * Selects the row that precedes the last selected row.
27152      * @param {Boolean} keepExisting (optional) True to keep existing selections
27153      */
27154     selectPrevious : function(keepExisting){
27155         if(this.last){
27156             this.selectRow(this.last-1, keepExisting);
27157             this.grid.getView().focusRow(this.last);
27158         }
27159     },
27160
27161     /**
27162      * Returns the selected records
27163      * @return {Array} Array of selected records
27164      */
27165     getSelections : function(){
27166         return [].concat(this.selections.items);
27167     },
27168
27169     /**
27170      * Returns the first selected record.
27171      * @return {Record}
27172      */
27173     getSelected : function(){
27174         return this.selections.itemAt(0);
27175     },
27176
27177
27178     /**
27179      * Clears all selections.
27180      */
27181     clearSelections : function(fast)
27182     {
27183         if(this.locked) {
27184             return;
27185         }
27186         if(fast !== true){
27187                 var ds = this.grid.store;
27188             var s = this.selections;
27189             s.each(function(r){
27190                 this.deselectRow(ds.indexOfId(r.id));
27191             }, this);
27192             s.clear();
27193         }else{
27194             this.selections.clear();
27195         }
27196         this.last = false;
27197     },
27198
27199
27200     /**
27201      * Selects all rows.
27202      */
27203     selectAll : function(){
27204         if(this.locked) {
27205             return;
27206         }
27207         this.selections.clear();
27208         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27209             this.selectRow(i, true);
27210         }
27211     },
27212
27213     /**
27214      * Returns True if there is a selection.
27215      * @return {Boolean}
27216      */
27217     hasSelection : function(){
27218         return this.selections.length > 0;
27219     },
27220
27221     /**
27222      * Returns True if the specified row is selected.
27223      * @param {Number/Record} record The record or index of the record to check
27224      * @return {Boolean}
27225      */
27226     isSelected : function(index){
27227             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27228         return (r && this.selections.key(r.id) ? true : false);
27229     },
27230
27231     /**
27232      * Returns True if the specified record id is selected.
27233      * @param {String} id The id of record to check
27234      * @return {Boolean}
27235      */
27236     isIdSelected : function(id){
27237         return (this.selections.key(id) ? true : false);
27238     },
27239
27240
27241     // private
27242     handleMouseDBClick : function(e, t){
27243         
27244     },
27245     // private
27246     handleMouseDown : function(e, t)
27247     {
27248             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27249         if(this.isLocked() || rowIndex < 0 ){
27250             return;
27251         };
27252         if(e.shiftKey && this.last !== false){
27253             var last = this.last;
27254             this.selectRange(last, rowIndex, e.ctrlKey);
27255             this.last = last; // reset the last
27256             t.focus();
27257     
27258         }else{
27259             var isSelected = this.isSelected(rowIndex);
27260             //Roo.log("select row:" + rowIndex);
27261             if(isSelected){
27262                 this.deselectRow(rowIndex);
27263             } else {
27264                         this.selectRow(rowIndex, true);
27265             }
27266     
27267             /*
27268                 if(e.button !== 0 && isSelected){
27269                 alert('rowIndex 2: ' + rowIndex);
27270                     view.focusRow(rowIndex);
27271                 }else if(e.ctrlKey && isSelected){
27272                     this.deselectRow(rowIndex);
27273                 }else if(!isSelected){
27274                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27275                     view.focusRow(rowIndex);
27276                 }
27277             */
27278         }
27279         this.fireEvent("afterselectionchange", this);
27280     },
27281     // private
27282     handleDragableRowClick :  function(grid, rowIndex, e) 
27283     {
27284         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27285             this.selectRow(rowIndex, false);
27286             grid.view.focusRow(rowIndex);
27287              this.fireEvent("afterselectionchange", this);
27288         }
27289     },
27290     
27291     /**
27292      * Selects multiple rows.
27293      * @param {Array} rows Array of the indexes of the row to select
27294      * @param {Boolean} keepExisting (optional) True to keep existing selections
27295      */
27296     selectRows : function(rows, keepExisting){
27297         if(!keepExisting){
27298             this.clearSelections();
27299         }
27300         for(var i = 0, len = rows.length; i < len; i++){
27301             this.selectRow(rows[i], true);
27302         }
27303     },
27304
27305     /**
27306      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27307      * @param {Number} startRow The index of the first row in the range
27308      * @param {Number} endRow The index of the last row in the range
27309      * @param {Boolean} keepExisting (optional) True to retain existing selections
27310      */
27311     selectRange : function(startRow, endRow, keepExisting){
27312         if(this.locked) {
27313             return;
27314         }
27315         if(!keepExisting){
27316             this.clearSelections();
27317         }
27318         if(startRow <= endRow){
27319             for(var i = startRow; i <= endRow; i++){
27320                 this.selectRow(i, true);
27321             }
27322         }else{
27323             for(var i = startRow; i >= endRow; i--){
27324                 this.selectRow(i, true);
27325             }
27326         }
27327     },
27328
27329     /**
27330      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27331      * @param {Number} startRow The index of the first row in the range
27332      * @param {Number} endRow The index of the last row in the range
27333      */
27334     deselectRange : function(startRow, endRow, preventViewNotify){
27335         if(this.locked) {
27336             return;
27337         }
27338         for(var i = startRow; i <= endRow; i++){
27339             this.deselectRow(i, preventViewNotify);
27340         }
27341     },
27342
27343     /**
27344      * Selects a row.
27345      * @param {Number} row The index of the row to select
27346      * @param {Boolean} keepExisting (optional) True to keep existing selections
27347      */
27348     selectRow : function(index, keepExisting, preventViewNotify)
27349     {
27350             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27351             return;
27352         }
27353         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27354             if(!keepExisting || this.singleSelect){
27355                 this.clearSelections();
27356             }
27357             
27358             var r = this.grid.store.getAt(index);
27359             //console.log('selectRow - record id :' + r.id);
27360             
27361             this.selections.add(r);
27362             this.last = this.lastActive = index;
27363             if(!preventViewNotify){
27364                 var proxy = new Roo.Element(
27365                                 this.grid.getRowDom(index)
27366                 );
27367                 proxy.addClass('bg-info info');
27368             }
27369             this.fireEvent("rowselect", this, index, r);
27370             this.fireEvent("selectionchange", this);
27371         }
27372     },
27373
27374     /**
27375      * Deselects a row.
27376      * @param {Number} row The index of the row to deselect
27377      */
27378     deselectRow : function(index, preventViewNotify)
27379     {
27380         if(this.locked) {
27381             return;
27382         }
27383         if(this.last == index){
27384             this.last = false;
27385         }
27386         if(this.lastActive == index){
27387             this.lastActive = false;
27388         }
27389         
27390         var r = this.grid.store.getAt(index);
27391         if (!r) {
27392             return;
27393         }
27394         
27395         this.selections.remove(r);
27396         //.console.log('deselectRow - record id :' + r.id);
27397         if(!preventViewNotify){
27398         
27399             var proxy = new Roo.Element(
27400                 this.grid.getRowDom(index)
27401             );
27402             proxy.removeClass('bg-info info');
27403         }
27404         this.fireEvent("rowdeselect", this, index);
27405         this.fireEvent("selectionchange", this);
27406     },
27407
27408     // private
27409     restoreLast : function(){
27410         if(this._last){
27411             this.last = this._last;
27412         }
27413     },
27414
27415     // private
27416     acceptsNav : function(row, col, cm){
27417         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27418     },
27419
27420     // private
27421     onEditorKey : function(field, e){
27422         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27423         if(k == e.TAB){
27424             e.stopEvent();
27425             ed.completeEdit();
27426             if(e.shiftKey){
27427                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27428             }else{
27429                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27430             }
27431         }else if(k == e.ENTER && !e.ctrlKey){
27432             e.stopEvent();
27433             ed.completeEdit();
27434             if(e.shiftKey){
27435                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27436             }else{
27437                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27438             }
27439         }else if(k == e.ESC){
27440             ed.cancelEdit();
27441         }
27442         if(newCell){
27443             g.startEditing(newCell[0], newCell[1]);
27444         }
27445     }
27446 });
27447 /*
27448  * Based on:
27449  * Ext JS Library 1.1.1
27450  * Copyright(c) 2006-2007, Ext JS, LLC.
27451  *
27452  * Originally Released Under LGPL - original licence link has changed is not relivant.
27453  *
27454  * Fork - LGPL
27455  * <script type="text/javascript">
27456  */
27457  
27458 /**
27459  * @class Roo.bootstrap.PagingToolbar
27460  * @extends Roo.bootstrap.NavSimplebar
27461  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27462  * @constructor
27463  * Create a new PagingToolbar
27464  * @param {Object} config The config object
27465  * @param {Roo.data.Store} store
27466  */
27467 Roo.bootstrap.PagingToolbar = function(config)
27468 {
27469     // old args format still supported... - xtype is prefered..
27470         // created from xtype...
27471     
27472     this.ds = config.dataSource;
27473     
27474     if (config.store && !this.ds) {
27475         this.store= Roo.factory(config.store, Roo.data);
27476         this.ds = this.store;
27477         this.ds.xmodule = this.xmodule || false;
27478     }
27479     
27480     this.toolbarItems = [];
27481     if (config.items) {
27482         this.toolbarItems = config.items;
27483     }
27484     
27485     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27486     
27487     this.cursor = 0;
27488     
27489     if (this.ds) { 
27490         this.bind(this.ds);
27491     }
27492     
27493     if (Roo.bootstrap.version == 4) {
27494         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27495     } else {
27496         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27497     }
27498     
27499 };
27500
27501 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27502     /**
27503      * @cfg {Roo.data.Store} dataSource
27504      * The underlying data store providing the paged data
27505      */
27506     /**
27507      * @cfg {String/HTMLElement/Element} container
27508      * container The id or element that will contain the toolbar
27509      */
27510     /**
27511      * @cfg {Boolean} displayInfo
27512      * True to display the displayMsg (defaults to false)
27513      */
27514     /**
27515      * @cfg {Number} pageSize
27516      * The number of records to display per page (defaults to 20)
27517      */
27518     pageSize: 20,
27519     /**
27520      * @cfg {String} displayMsg
27521      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27522      */
27523     displayMsg : 'Displaying {0} - {1} of {2}',
27524     /**
27525      * @cfg {String} emptyMsg
27526      * The message to display when no records are found (defaults to "No data to display")
27527      */
27528     emptyMsg : 'No data to display',
27529     /**
27530      * Customizable piece of the default paging text (defaults to "Page")
27531      * @type String
27532      */
27533     beforePageText : "Page",
27534     /**
27535      * Customizable piece of the default paging text (defaults to "of %0")
27536      * @type String
27537      */
27538     afterPageText : "of {0}",
27539     /**
27540      * Customizable piece of the default paging text (defaults to "First Page")
27541      * @type String
27542      */
27543     firstText : "First Page",
27544     /**
27545      * Customizable piece of the default paging text (defaults to "Previous Page")
27546      * @type String
27547      */
27548     prevText : "Previous Page",
27549     /**
27550      * Customizable piece of the default paging text (defaults to "Next Page")
27551      * @type String
27552      */
27553     nextText : "Next Page",
27554     /**
27555      * Customizable piece of the default paging text (defaults to "Last Page")
27556      * @type String
27557      */
27558     lastText : "Last Page",
27559     /**
27560      * Customizable piece of the default paging text (defaults to "Refresh")
27561      * @type String
27562      */
27563     refreshText : "Refresh",
27564
27565     buttons : false,
27566     // private
27567     onRender : function(ct, position) 
27568     {
27569         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27570         this.navgroup.parentId = this.id;
27571         this.navgroup.onRender(this.el, null);
27572         // add the buttons to the navgroup
27573         
27574         if(this.displayInfo){
27575             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27576             this.displayEl = this.el.select('.x-paging-info', true).first();
27577 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27578 //            this.displayEl = navel.el.select('span',true).first();
27579         }
27580         
27581         var _this = this;
27582         
27583         if(this.buttons){
27584             Roo.each(_this.buttons, function(e){ // this might need to use render????
27585                Roo.factory(e).render(_this.el);
27586             });
27587         }
27588             
27589         Roo.each(_this.toolbarItems, function(e) {
27590             _this.navgroup.addItem(e);
27591         });
27592         
27593         
27594         this.first = this.navgroup.addItem({
27595             tooltip: this.firstText,
27596             cls: "prev btn-outline-secondary",
27597             html : ' <i class="fa fa-step-backward"></i>',
27598             disabled: true,
27599             preventDefault: true,
27600             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27601         });
27602         
27603         this.prev =  this.navgroup.addItem({
27604             tooltip: this.prevText,
27605             cls: "prev btn-outline-secondary",
27606             html : ' <i class="fa fa-backward"></i>',
27607             disabled: true,
27608             preventDefault: true,
27609             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27610         });
27611     //this.addSeparator();
27612         
27613         
27614         var field = this.navgroup.addItem( {
27615             tagtype : 'span',
27616             cls : 'x-paging-position  btn-outline-secondary',
27617              disabled: true,
27618             html : this.beforePageText  +
27619                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27620                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27621          } ); //?? escaped?
27622         
27623         this.field = field.el.select('input', true).first();
27624         this.field.on("keydown", this.onPagingKeydown, this);
27625         this.field.on("focus", function(){this.dom.select();});
27626     
27627     
27628         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27629         //this.field.setHeight(18);
27630         //this.addSeparator();
27631         this.next = this.navgroup.addItem({
27632             tooltip: this.nextText,
27633             cls: "next btn-outline-secondary",
27634             html : ' <i class="fa fa-forward"></i>',
27635             disabled: true,
27636             preventDefault: true,
27637             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27638         });
27639         this.last = this.navgroup.addItem({
27640             tooltip: this.lastText,
27641             html : ' <i class="fa fa-step-forward"></i>',
27642             cls: "next btn-outline-secondary",
27643             disabled: true,
27644             preventDefault: true,
27645             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27646         });
27647     //this.addSeparator();
27648         this.loading = this.navgroup.addItem({
27649             tooltip: this.refreshText,
27650             cls: "btn-outline-secondary",
27651             html : ' <i class="fa fa-refresh"></i>',
27652             preventDefault: true,
27653             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27654         });
27655         
27656     },
27657
27658     // private
27659     updateInfo : function(){
27660         if(this.displayEl){
27661             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27662             var msg = count == 0 ?
27663                 this.emptyMsg :
27664                 String.format(
27665                     this.displayMsg,
27666                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27667                 );
27668             this.displayEl.update(msg);
27669         }
27670     },
27671
27672     // private
27673     onLoad : function(ds, r, o)
27674     {
27675         this.cursor = o.params && o.params.start ? o.params.start : 0;
27676         
27677         var d = this.getPageData(),
27678             ap = d.activePage,
27679             ps = d.pages;
27680         
27681         
27682         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27683         this.field.dom.value = ap;
27684         this.first.setDisabled(ap == 1);
27685         this.prev.setDisabled(ap == 1);
27686         this.next.setDisabled(ap == ps);
27687         this.last.setDisabled(ap == ps);
27688         this.loading.enable();
27689         this.updateInfo();
27690     },
27691
27692     // private
27693     getPageData : function(){
27694         var total = this.ds.getTotalCount();
27695         return {
27696             total : total,
27697             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27698             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27699         };
27700     },
27701
27702     // private
27703     onLoadError : function(){
27704         this.loading.enable();
27705     },
27706
27707     // private
27708     onPagingKeydown : function(e){
27709         var k = e.getKey();
27710         var d = this.getPageData();
27711         if(k == e.RETURN){
27712             var v = this.field.dom.value, pageNum;
27713             if(!v || isNaN(pageNum = parseInt(v, 10))){
27714                 this.field.dom.value = d.activePage;
27715                 return;
27716             }
27717             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27718             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27719             e.stopEvent();
27720         }
27721         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))
27722         {
27723           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27724           this.field.dom.value = pageNum;
27725           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27726           e.stopEvent();
27727         }
27728         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27729         {
27730           var v = this.field.dom.value, pageNum; 
27731           var increment = (e.shiftKey) ? 10 : 1;
27732           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27733                 increment *= -1;
27734           }
27735           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27736             this.field.dom.value = d.activePage;
27737             return;
27738           }
27739           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27740           {
27741             this.field.dom.value = parseInt(v, 10) + increment;
27742             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27743             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27744           }
27745           e.stopEvent();
27746         }
27747     },
27748
27749     // private
27750     beforeLoad : function(){
27751         if(this.loading){
27752             this.loading.disable();
27753         }
27754     },
27755
27756     // private
27757     onClick : function(which){
27758         
27759         var ds = this.ds;
27760         if (!ds) {
27761             return;
27762         }
27763         
27764         switch(which){
27765             case "first":
27766                 ds.load({params:{start: 0, limit: this.pageSize}});
27767             break;
27768             case "prev":
27769                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27770             break;
27771             case "next":
27772                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27773             break;
27774             case "last":
27775                 var total = ds.getTotalCount();
27776                 var extra = total % this.pageSize;
27777                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27778                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27779             break;
27780             case "refresh":
27781                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27782             break;
27783         }
27784     },
27785
27786     /**
27787      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27788      * @param {Roo.data.Store} store The data store to unbind
27789      */
27790     unbind : function(ds){
27791         ds.un("beforeload", this.beforeLoad, this);
27792         ds.un("load", this.onLoad, this);
27793         ds.un("loadexception", this.onLoadError, this);
27794         ds.un("remove", this.updateInfo, this);
27795         ds.un("add", this.updateInfo, this);
27796         this.ds = undefined;
27797     },
27798
27799     /**
27800      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27801      * @param {Roo.data.Store} store The data store to bind
27802      */
27803     bind : function(ds){
27804         ds.on("beforeload", this.beforeLoad, this);
27805         ds.on("load", this.onLoad, this);
27806         ds.on("loadexception", this.onLoadError, this);
27807         ds.on("remove", this.updateInfo, this);
27808         ds.on("add", this.updateInfo, this);
27809         this.ds = ds;
27810     }
27811 });/*
27812  * - LGPL
27813  *
27814  * element
27815  * 
27816  */
27817
27818 /**
27819  * @class Roo.bootstrap.MessageBar
27820  * @extends Roo.bootstrap.Component
27821  * Bootstrap MessageBar class
27822  * @cfg {String} html contents of the MessageBar
27823  * @cfg {String} weight (info | success | warning | danger) default info
27824  * @cfg {String} beforeClass insert the bar before the given class
27825  * @cfg {Boolean} closable (true | false) default false
27826  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27827  * 
27828  * @constructor
27829  * Create a new Element
27830  * @param {Object} config The config object
27831  */
27832
27833 Roo.bootstrap.MessageBar = function(config){
27834     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27835 };
27836
27837 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27838     
27839     html: '',
27840     weight: 'info',
27841     closable: false,
27842     fixed: false,
27843     beforeClass: 'bootstrap-sticky-wrap',
27844     
27845     getAutoCreate : function(){
27846         
27847         var cfg = {
27848             tag: 'div',
27849             cls: 'alert alert-dismissable alert-' + this.weight,
27850             cn: [
27851                 {
27852                     tag: 'span',
27853                     cls: 'message',
27854                     html: this.html || ''
27855                 }
27856             ]
27857         };
27858         
27859         if(this.fixed){
27860             cfg.cls += ' alert-messages-fixed';
27861         }
27862         
27863         if(this.closable){
27864             cfg.cn.push({
27865                 tag: 'button',
27866                 cls: 'close',
27867                 html: 'x'
27868             });
27869         }
27870         
27871         return cfg;
27872     },
27873     
27874     onRender : function(ct, position)
27875     {
27876         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27877         
27878         if(!this.el){
27879             var cfg = Roo.apply({},  this.getAutoCreate());
27880             cfg.id = Roo.id();
27881             
27882             if (this.cls) {
27883                 cfg.cls += ' ' + this.cls;
27884             }
27885             if (this.style) {
27886                 cfg.style = this.style;
27887             }
27888             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27889             
27890             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27891         }
27892         
27893         this.el.select('>button.close').on('click', this.hide, this);
27894         
27895     },
27896     
27897     show : function()
27898     {
27899         if (!this.rendered) {
27900             this.render();
27901         }
27902         
27903         this.el.show();
27904         
27905         this.fireEvent('show', this);
27906         
27907     },
27908     
27909     hide : function()
27910     {
27911         if (!this.rendered) {
27912             this.render();
27913         }
27914         
27915         this.el.hide();
27916         
27917         this.fireEvent('hide', this);
27918     },
27919     
27920     update : function()
27921     {
27922 //        var e = this.el.dom.firstChild;
27923 //        
27924 //        if(this.closable){
27925 //            e = e.nextSibling;
27926 //        }
27927 //        
27928 //        e.data = this.html || '';
27929
27930         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27931     }
27932    
27933 });
27934
27935  
27936
27937      /*
27938  * - LGPL
27939  *
27940  * Graph
27941  * 
27942  */
27943
27944
27945 /**
27946  * @class Roo.bootstrap.Graph
27947  * @extends Roo.bootstrap.Component
27948  * Bootstrap Graph class
27949 > Prameters
27950  -sm {number} sm 4
27951  -md {number} md 5
27952  @cfg {String} graphtype  bar | vbar | pie
27953  @cfg {number} g_x coodinator | centre x (pie)
27954  @cfg {number} g_y coodinator | centre y (pie)
27955  @cfg {number} g_r radius (pie)
27956  @cfg {number} g_height height of the chart (respected by all elements in the set)
27957  @cfg {number} g_width width of the chart (respected by all elements in the set)
27958  @cfg {Object} title The title of the chart
27959     
27960  -{Array}  values
27961  -opts (object) options for the chart 
27962      o {
27963      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27964      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27965      o vgutter (number)
27966      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.
27967      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27968      o to
27969      o stretch (boolean)
27970      o }
27971  -opts (object) options for the pie
27972      o{
27973      o cut
27974      o startAngle (number)
27975      o endAngle (number)
27976      } 
27977  *
27978  * @constructor
27979  * Create a new Input
27980  * @param {Object} config The config object
27981  */
27982
27983 Roo.bootstrap.Graph = function(config){
27984     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27985     
27986     this.addEvents({
27987         // img events
27988         /**
27989          * @event click
27990          * The img click event for the img.
27991          * @param {Roo.EventObject} e
27992          */
27993         "click" : true
27994     });
27995 };
27996
27997 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27998     
27999     sm: 4,
28000     md: 5,
28001     graphtype: 'bar',
28002     g_height: 250,
28003     g_width: 400,
28004     g_x: 50,
28005     g_y: 50,
28006     g_r: 30,
28007     opts:{
28008         //g_colors: this.colors,
28009         g_type: 'soft',
28010         g_gutter: '20%'
28011
28012     },
28013     title : false,
28014
28015     getAutoCreate : function(){
28016         
28017         var cfg = {
28018             tag: 'div',
28019             html : null
28020         };
28021         
28022         
28023         return  cfg;
28024     },
28025
28026     onRender : function(ct,position){
28027         
28028         
28029         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28030         
28031         if (typeof(Raphael) == 'undefined') {
28032             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28033             return;
28034         }
28035         
28036         this.raphael = Raphael(this.el.dom);
28037         
28038                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28039                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28040                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28041                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28042                 /*
28043                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28044                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28045                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28046                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28047                 
28048                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28049                 r.barchart(330, 10, 300, 220, data1);
28050                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28051                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28052                 */
28053                 
28054                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28055                 // r.barchart(30, 30, 560, 250,  xdata, {
28056                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28057                 //     axis : "0 0 1 1",
28058                 //     axisxlabels :  xdata
28059                 //     //yvalues : cols,
28060                    
28061                 // });
28062 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28063 //        
28064 //        this.load(null,xdata,{
28065 //                axis : "0 0 1 1",
28066 //                axisxlabels :  xdata
28067 //                });
28068
28069     },
28070
28071     load : function(graphtype,xdata,opts)
28072     {
28073         this.raphael.clear();
28074         if(!graphtype) {
28075             graphtype = this.graphtype;
28076         }
28077         if(!opts){
28078             opts = this.opts;
28079         }
28080         var r = this.raphael,
28081             fin = function () {
28082                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28083             },
28084             fout = function () {
28085                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28086             },
28087             pfin = function() {
28088                 this.sector.stop();
28089                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28090
28091                 if (this.label) {
28092                     this.label[0].stop();
28093                     this.label[0].attr({ r: 7.5 });
28094                     this.label[1].attr({ "font-weight": 800 });
28095                 }
28096             },
28097             pfout = function() {
28098                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28099
28100                 if (this.label) {
28101                     this.label[0].animate({ r: 5 }, 500, "bounce");
28102                     this.label[1].attr({ "font-weight": 400 });
28103                 }
28104             };
28105
28106         switch(graphtype){
28107             case 'bar':
28108                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28109                 break;
28110             case 'hbar':
28111                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28112                 break;
28113             case 'pie':
28114 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28115 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28116 //            
28117                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28118                 
28119                 break;
28120
28121         }
28122         
28123         if(this.title){
28124             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28125         }
28126         
28127     },
28128     
28129     setTitle: function(o)
28130     {
28131         this.title = o;
28132     },
28133     
28134     initEvents: function() {
28135         
28136         if(!this.href){
28137             this.el.on('click', this.onClick, this);
28138         }
28139     },
28140     
28141     onClick : function(e)
28142     {
28143         Roo.log('img onclick');
28144         this.fireEvent('click', this, e);
28145     }
28146    
28147 });
28148
28149  
28150 /*
28151  * - LGPL
28152  *
28153  * numberBox
28154  * 
28155  */
28156 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28157
28158 /**
28159  * @class Roo.bootstrap.dash.NumberBox
28160  * @extends Roo.bootstrap.Component
28161  * Bootstrap NumberBox class
28162  * @cfg {String} headline Box headline
28163  * @cfg {String} content Box content
28164  * @cfg {String} icon Box icon
28165  * @cfg {String} footer Footer text
28166  * @cfg {String} fhref Footer href
28167  * 
28168  * @constructor
28169  * Create a new NumberBox
28170  * @param {Object} config The config object
28171  */
28172
28173
28174 Roo.bootstrap.dash.NumberBox = function(config){
28175     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28176     
28177 };
28178
28179 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28180     
28181     headline : '',
28182     content : '',
28183     icon : '',
28184     footer : '',
28185     fhref : '',
28186     ficon : '',
28187     
28188     getAutoCreate : function(){
28189         
28190         var cfg = {
28191             tag : 'div',
28192             cls : 'small-box ',
28193             cn : [
28194                 {
28195                     tag : 'div',
28196                     cls : 'inner',
28197                     cn :[
28198                         {
28199                             tag : 'h3',
28200                             cls : 'roo-headline',
28201                             html : this.headline
28202                         },
28203                         {
28204                             tag : 'p',
28205                             cls : 'roo-content',
28206                             html : this.content
28207                         }
28208                     ]
28209                 }
28210             ]
28211         };
28212         
28213         if(this.icon){
28214             cfg.cn.push({
28215                 tag : 'div',
28216                 cls : 'icon',
28217                 cn :[
28218                     {
28219                         tag : 'i',
28220                         cls : 'ion ' + this.icon
28221                     }
28222                 ]
28223             });
28224         }
28225         
28226         if(this.footer){
28227             var footer = {
28228                 tag : 'a',
28229                 cls : 'small-box-footer',
28230                 href : this.fhref || '#',
28231                 html : this.footer
28232             };
28233             
28234             cfg.cn.push(footer);
28235             
28236         }
28237         
28238         return  cfg;
28239     },
28240
28241     onRender : function(ct,position){
28242         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28243
28244
28245        
28246                 
28247     },
28248
28249     setHeadline: function (value)
28250     {
28251         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28252     },
28253     
28254     setFooter: function (value, href)
28255     {
28256         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28257         
28258         if(href){
28259             this.el.select('a.small-box-footer',true).first().attr('href', href);
28260         }
28261         
28262     },
28263
28264     setContent: function (value)
28265     {
28266         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28267     },
28268
28269     initEvents: function() 
28270     {   
28271         
28272     }
28273     
28274 });
28275
28276  
28277 /*
28278  * - LGPL
28279  *
28280  * TabBox
28281  * 
28282  */
28283 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28284
28285 /**
28286  * @class Roo.bootstrap.dash.TabBox
28287  * @extends Roo.bootstrap.Component
28288  * Bootstrap TabBox class
28289  * @cfg {String} title Title of the TabBox
28290  * @cfg {String} icon Icon of the TabBox
28291  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28292  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28293  * 
28294  * @constructor
28295  * Create a new TabBox
28296  * @param {Object} config The config object
28297  */
28298
28299
28300 Roo.bootstrap.dash.TabBox = function(config){
28301     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28302     this.addEvents({
28303         // raw events
28304         /**
28305          * @event addpane
28306          * When a pane is added
28307          * @param {Roo.bootstrap.dash.TabPane} pane
28308          */
28309         "addpane" : true,
28310         /**
28311          * @event activatepane
28312          * When a pane is activated
28313          * @param {Roo.bootstrap.dash.TabPane} pane
28314          */
28315         "activatepane" : true
28316         
28317          
28318     });
28319     
28320     this.panes = [];
28321 };
28322
28323 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28324
28325     title : '',
28326     icon : false,
28327     showtabs : true,
28328     tabScrollable : false,
28329     
28330     getChildContainer : function()
28331     {
28332         return this.el.select('.tab-content', true).first();
28333     },
28334     
28335     getAutoCreate : function(){
28336         
28337         var header = {
28338             tag: 'li',
28339             cls: 'pull-left header',
28340             html: this.title,
28341             cn : []
28342         };
28343         
28344         if(this.icon){
28345             header.cn.push({
28346                 tag: 'i',
28347                 cls: 'fa ' + this.icon
28348             });
28349         }
28350         
28351         var h = {
28352             tag: 'ul',
28353             cls: 'nav nav-tabs pull-right',
28354             cn: [
28355                 header
28356             ]
28357         };
28358         
28359         if(this.tabScrollable){
28360             h = {
28361                 tag: 'div',
28362                 cls: 'tab-header',
28363                 cn: [
28364                     {
28365                         tag: 'ul',
28366                         cls: 'nav nav-tabs pull-right',
28367                         cn: [
28368                             header
28369                         ]
28370                     }
28371                 ]
28372             };
28373         }
28374         
28375         var cfg = {
28376             tag: 'div',
28377             cls: 'nav-tabs-custom',
28378             cn: [
28379                 h,
28380                 {
28381                     tag: 'div',
28382                     cls: 'tab-content no-padding',
28383                     cn: []
28384                 }
28385             ]
28386         };
28387
28388         return  cfg;
28389     },
28390     initEvents : function()
28391     {
28392         //Roo.log('add add pane handler');
28393         this.on('addpane', this.onAddPane, this);
28394     },
28395      /**
28396      * Updates the box title
28397      * @param {String} html to set the title to.
28398      */
28399     setTitle : function(value)
28400     {
28401         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28402     },
28403     onAddPane : function(pane)
28404     {
28405         this.panes.push(pane);
28406         //Roo.log('addpane');
28407         //Roo.log(pane);
28408         // tabs are rendere left to right..
28409         if(!this.showtabs){
28410             return;
28411         }
28412         
28413         var ctr = this.el.select('.nav-tabs', true).first();
28414          
28415          
28416         var existing = ctr.select('.nav-tab',true);
28417         var qty = existing.getCount();;
28418         
28419         
28420         var tab = ctr.createChild({
28421             tag : 'li',
28422             cls : 'nav-tab' + (qty ? '' : ' active'),
28423             cn : [
28424                 {
28425                     tag : 'a',
28426                     href:'#',
28427                     html : pane.title
28428                 }
28429             ]
28430         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28431         pane.tab = tab;
28432         
28433         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28434         if (!qty) {
28435             pane.el.addClass('active');
28436         }
28437         
28438                 
28439     },
28440     onTabClick : function(ev,un,ob,pane)
28441     {
28442         //Roo.log('tab - prev default');
28443         ev.preventDefault();
28444         
28445         
28446         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28447         pane.tab.addClass('active');
28448         //Roo.log(pane.title);
28449         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28450         // technically we should have a deactivate event.. but maybe add later.
28451         // and it should not de-activate the selected tab...
28452         this.fireEvent('activatepane', pane);
28453         pane.el.addClass('active');
28454         pane.fireEvent('activate');
28455         
28456         
28457     },
28458     
28459     getActivePane : function()
28460     {
28461         var r = false;
28462         Roo.each(this.panes, function(p) {
28463             if(p.el.hasClass('active')){
28464                 r = p;
28465                 return false;
28466             }
28467             
28468             return;
28469         });
28470         
28471         return r;
28472     }
28473     
28474     
28475 });
28476
28477  
28478 /*
28479  * - LGPL
28480  *
28481  * Tab pane
28482  * 
28483  */
28484 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28485 /**
28486  * @class Roo.bootstrap.TabPane
28487  * @extends Roo.bootstrap.Component
28488  * Bootstrap TabPane class
28489  * @cfg {Boolean} active (false | true) Default false
28490  * @cfg {String} title title of panel
28491
28492  * 
28493  * @constructor
28494  * Create a new TabPane
28495  * @param {Object} config The config object
28496  */
28497
28498 Roo.bootstrap.dash.TabPane = function(config){
28499     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28500     
28501     this.addEvents({
28502         // raw events
28503         /**
28504          * @event activate
28505          * When a pane is activated
28506          * @param {Roo.bootstrap.dash.TabPane} pane
28507          */
28508         "activate" : true
28509          
28510     });
28511 };
28512
28513 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28514     
28515     active : false,
28516     title : '',
28517     
28518     // the tabBox that this is attached to.
28519     tab : false,
28520      
28521     getAutoCreate : function() 
28522     {
28523         var cfg = {
28524             tag: 'div',
28525             cls: 'tab-pane'
28526         };
28527         
28528         if(this.active){
28529             cfg.cls += ' active';
28530         }
28531         
28532         return cfg;
28533     },
28534     initEvents  : function()
28535     {
28536         //Roo.log('trigger add pane handler');
28537         this.parent().fireEvent('addpane', this)
28538     },
28539     
28540      /**
28541      * Updates the tab title 
28542      * @param {String} html to set the title to.
28543      */
28544     setTitle: function(str)
28545     {
28546         if (!this.tab) {
28547             return;
28548         }
28549         this.title = str;
28550         this.tab.select('a', true).first().dom.innerHTML = str;
28551         
28552     }
28553     
28554     
28555     
28556 });
28557
28558  
28559
28560
28561  /*
28562  * - LGPL
28563  *
28564  * menu
28565  * 
28566  */
28567 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28568
28569 /**
28570  * @class Roo.bootstrap.menu.Menu
28571  * @extends Roo.bootstrap.Component
28572  * Bootstrap Menu class - container for Menu
28573  * @cfg {String} html Text of the menu
28574  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28575  * @cfg {String} icon Font awesome icon
28576  * @cfg {String} pos Menu align to (top | bottom) default bottom
28577  * 
28578  * 
28579  * @constructor
28580  * Create a new Menu
28581  * @param {Object} config The config object
28582  */
28583
28584
28585 Roo.bootstrap.menu.Menu = function(config){
28586     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28587     
28588     this.addEvents({
28589         /**
28590          * @event beforeshow
28591          * Fires before this menu is displayed
28592          * @param {Roo.bootstrap.menu.Menu} this
28593          */
28594         beforeshow : true,
28595         /**
28596          * @event beforehide
28597          * Fires before this menu is hidden
28598          * @param {Roo.bootstrap.menu.Menu} this
28599          */
28600         beforehide : true,
28601         /**
28602          * @event show
28603          * Fires after this menu is displayed
28604          * @param {Roo.bootstrap.menu.Menu} this
28605          */
28606         show : true,
28607         /**
28608          * @event hide
28609          * Fires after this menu is hidden
28610          * @param {Roo.bootstrap.menu.Menu} this
28611          */
28612         hide : true,
28613         /**
28614          * @event click
28615          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28616          * @param {Roo.bootstrap.menu.Menu} this
28617          * @param {Roo.EventObject} e
28618          */
28619         click : true
28620     });
28621     
28622 };
28623
28624 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28625     
28626     submenu : false,
28627     html : '',
28628     weight : 'default',
28629     icon : false,
28630     pos : 'bottom',
28631     
28632     
28633     getChildContainer : function() {
28634         if(this.isSubMenu){
28635             return this.el;
28636         }
28637         
28638         return this.el.select('ul.dropdown-menu', true).first();  
28639     },
28640     
28641     getAutoCreate : function()
28642     {
28643         var text = [
28644             {
28645                 tag : 'span',
28646                 cls : 'roo-menu-text',
28647                 html : this.html
28648             }
28649         ];
28650         
28651         if(this.icon){
28652             text.unshift({
28653                 tag : 'i',
28654                 cls : 'fa ' + this.icon
28655             })
28656         }
28657         
28658         
28659         var cfg = {
28660             tag : 'div',
28661             cls : 'btn-group',
28662             cn : [
28663                 {
28664                     tag : 'button',
28665                     cls : 'dropdown-button btn btn-' + this.weight,
28666                     cn : text
28667                 },
28668                 {
28669                     tag : 'button',
28670                     cls : 'dropdown-toggle btn btn-' + this.weight,
28671                     cn : [
28672                         {
28673                             tag : 'span',
28674                             cls : 'caret'
28675                         }
28676                     ]
28677                 },
28678                 {
28679                     tag : 'ul',
28680                     cls : 'dropdown-menu'
28681                 }
28682             ]
28683             
28684         };
28685         
28686         if(this.pos == 'top'){
28687             cfg.cls += ' dropup';
28688         }
28689         
28690         if(this.isSubMenu){
28691             cfg = {
28692                 tag : 'ul',
28693                 cls : 'dropdown-menu'
28694             }
28695         }
28696         
28697         return cfg;
28698     },
28699     
28700     onRender : function(ct, position)
28701     {
28702         this.isSubMenu = ct.hasClass('dropdown-submenu');
28703         
28704         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28705     },
28706     
28707     initEvents : function() 
28708     {
28709         if(this.isSubMenu){
28710             return;
28711         }
28712         
28713         this.hidden = true;
28714         
28715         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28716         this.triggerEl.on('click', this.onTriggerPress, this);
28717         
28718         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28719         this.buttonEl.on('click', this.onClick, this);
28720         
28721     },
28722     
28723     list : function()
28724     {
28725         if(this.isSubMenu){
28726             return this.el;
28727         }
28728         
28729         return this.el.select('ul.dropdown-menu', true).first();
28730     },
28731     
28732     onClick : function(e)
28733     {
28734         this.fireEvent("click", this, e);
28735     },
28736     
28737     onTriggerPress  : function(e)
28738     {   
28739         if (this.isVisible()) {
28740             this.hide();
28741         } else {
28742             this.show();
28743         }
28744     },
28745     
28746     isVisible : function(){
28747         return !this.hidden;
28748     },
28749     
28750     show : function()
28751     {
28752         this.fireEvent("beforeshow", this);
28753         
28754         this.hidden = false;
28755         this.el.addClass('open');
28756         
28757         Roo.get(document).on("mouseup", this.onMouseUp, this);
28758         
28759         this.fireEvent("show", this);
28760         
28761         
28762     },
28763     
28764     hide : function()
28765     {
28766         this.fireEvent("beforehide", this);
28767         
28768         this.hidden = true;
28769         this.el.removeClass('open');
28770         
28771         Roo.get(document).un("mouseup", this.onMouseUp);
28772         
28773         this.fireEvent("hide", this);
28774     },
28775     
28776     onMouseUp : function()
28777     {
28778         this.hide();
28779     }
28780     
28781 });
28782
28783  
28784  /*
28785  * - LGPL
28786  *
28787  * menu item
28788  * 
28789  */
28790 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28791
28792 /**
28793  * @class Roo.bootstrap.menu.Item
28794  * @extends Roo.bootstrap.Component
28795  * Bootstrap MenuItem class
28796  * @cfg {Boolean} submenu (true | false) default false
28797  * @cfg {String} html text of the item
28798  * @cfg {String} href the link
28799  * @cfg {Boolean} disable (true | false) default false
28800  * @cfg {Boolean} preventDefault (true | false) default true
28801  * @cfg {String} icon Font awesome icon
28802  * @cfg {String} pos Submenu align to (left | right) default right 
28803  * 
28804  * 
28805  * @constructor
28806  * Create a new Item
28807  * @param {Object} config The config object
28808  */
28809
28810
28811 Roo.bootstrap.menu.Item = function(config){
28812     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28813     this.addEvents({
28814         /**
28815          * @event mouseover
28816          * Fires when the mouse is hovering over this menu
28817          * @param {Roo.bootstrap.menu.Item} this
28818          * @param {Roo.EventObject} e
28819          */
28820         mouseover : true,
28821         /**
28822          * @event mouseout
28823          * Fires when the mouse exits this menu
28824          * @param {Roo.bootstrap.menu.Item} this
28825          * @param {Roo.EventObject} e
28826          */
28827         mouseout : true,
28828         // raw events
28829         /**
28830          * @event click
28831          * The raw click event for the entire grid.
28832          * @param {Roo.EventObject} e
28833          */
28834         click : true
28835     });
28836 };
28837
28838 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28839     
28840     submenu : false,
28841     href : '',
28842     html : '',
28843     preventDefault: true,
28844     disable : false,
28845     icon : false,
28846     pos : 'right',
28847     
28848     getAutoCreate : function()
28849     {
28850         var text = [
28851             {
28852                 tag : 'span',
28853                 cls : 'roo-menu-item-text',
28854                 html : this.html
28855             }
28856         ];
28857         
28858         if(this.icon){
28859             text.unshift({
28860                 tag : 'i',
28861                 cls : 'fa ' + this.icon
28862             })
28863         }
28864         
28865         var cfg = {
28866             tag : 'li',
28867             cn : [
28868                 {
28869                     tag : 'a',
28870                     href : this.href || '#',
28871                     cn : text
28872                 }
28873             ]
28874         };
28875         
28876         if(this.disable){
28877             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28878         }
28879         
28880         if(this.submenu){
28881             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28882             
28883             if(this.pos == 'left'){
28884                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28885             }
28886         }
28887         
28888         return cfg;
28889     },
28890     
28891     initEvents : function() 
28892     {
28893         this.el.on('mouseover', this.onMouseOver, this);
28894         this.el.on('mouseout', this.onMouseOut, this);
28895         
28896         this.el.select('a', true).first().on('click', this.onClick, this);
28897         
28898     },
28899     
28900     onClick : function(e)
28901     {
28902         if(this.preventDefault){
28903             e.preventDefault();
28904         }
28905         
28906         this.fireEvent("click", this, e);
28907     },
28908     
28909     onMouseOver : function(e)
28910     {
28911         if(this.submenu && this.pos == 'left'){
28912             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28913         }
28914         
28915         this.fireEvent("mouseover", this, e);
28916     },
28917     
28918     onMouseOut : function(e)
28919     {
28920         this.fireEvent("mouseout", this, e);
28921     }
28922 });
28923
28924  
28925
28926  /*
28927  * - LGPL
28928  *
28929  * menu separator
28930  * 
28931  */
28932 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28933
28934 /**
28935  * @class Roo.bootstrap.menu.Separator
28936  * @extends Roo.bootstrap.Component
28937  * Bootstrap Separator class
28938  * 
28939  * @constructor
28940  * Create a new Separator
28941  * @param {Object} config The config object
28942  */
28943
28944
28945 Roo.bootstrap.menu.Separator = function(config){
28946     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28947 };
28948
28949 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28950     
28951     getAutoCreate : function(){
28952         var cfg = {
28953             tag : 'li',
28954             cls: 'divider'
28955         };
28956         
28957         return cfg;
28958     }
28959    
28960 });
28961
28962  
28963
28964  /*
28965  * - LGPL
28966  *
28967  * Tooltip
28968  * 
28969  */
28970
28971 /**
28972  * @class Roo.bootstrap.Tooltip
28973  * Bootstrap Tooltip class
28974  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28975  * to determine which dom element triggers the tooltip.
28976  * 
28977  * It needs to add support for additional attributes like tooltip-position
28978  * 
28979  * @constructor
28980  * Create a new Toolti
28981  * @param {Object} config The config object
28982  */
28983
28984 Roo.bootstrap.Tooltip = function(config){
28985     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28986     
28987     this.alignment = Roo.bootstrap.Tooltip.alignment;
28988     
28989     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28990         this.alignment = config.alignment;
28991     }
28992     
28993 };
28994
28995 Roo.apply(Roo.bootstrap.Tooltip, {
28996     /**
28997      * @function init initialize tooltip monitoring.
28998      * @static
28999      */
29000     currentEl : false,
29001     currentTip : false,
29002     currentRegion : false,
29003     
29004     //  init : delay?
29005     
29006     init : function()
29007     {
29008         Roo.get(document).on('mouseover', this.enter ,this);
29009         Roo.get(document).on('mouseout', this.leave, this);
29010          
29011         
29012         this.currentTip = new Roo.bootstrap.Tooltip();
29013     },
29014     
29015     enter : function(ev)
29016     {
29017         var dom = ev.getTarget();
29018         
29019         //Roo.log(['enter',dom]);
29020         var el = Roo.fly(dom);
29021         if (this.currentEl) {
29022             //Roo.log(dom);
29023             //Roo.log(this.currentEl);
29024             //Roo.log(this.currentEl.contains(dom));
29025             if (this.currentEl == el) {
29026                 return;
29027             }
29028             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29029                 return;
29030             }
29031
29032         }
29033         
29034         if (this.currentTip.el) {
29035             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29036         }    
29037         //Roo.log(ev);
29038         
29039         if(!el || el.dom == document){
29040             return;
29041         }
29042         
29043         var bindEl = el;
29044         
29045         // you can not look for children, as if el is the body.. then everythign is the child..
29046         if (!el.attr('tooltip')) { //
29047             if (!el.select("[tooltip]").elements.length) {
29048                 return;
29049             }
29050             // is the mouse over this child...?
29051             bindEl = el.select("[tooltip]").first();
29052             var xy = ev.getXY();
29053             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29054                 //Roo.log("not in region.");
29055                 return;
29056             }
29057             //Roo.log("child element over..");
29058             
29059         }
29060         this.currentEl = bindEl;
29061         this.currentTip.bind(bindEl);
29062         this.currentRegion = Roo.lib.Region.getRegion(dom);
29063         this.currentTip.enter();
29064         
29065     },
29066     leave : function(ev)
29067     {
29068         var dom = ev.getTarget();
29069         //Roo.log(['leave',dom]);
29070         if (!this.currentEl) {
29071             return;
29072         }
29073         
29074         
29075         if (dom != this.currentEl.dom) {
29076             return;
29077         }
29078         var xy = ev.getXY();
29079         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29080             return;
29081         }
29082         // only activate leave if mouse cursor is outside... bounding box..
29083         
29084         
29085         
29086         
29087         if (this.currentTip) {
29088             this.currentTip.leave();
29089         }
29090         //Roo.log('clear currentEl');
29091         this.currentEl = false;
29092         
29093         
29094     },
29095     alignment : {
29096         'left' : ['r-l', [-2,0], 'right'],
29097         'right' : ['l-r', [2,0], 'left'],
29098         'bottom' : ['t-b', [0,2], 'top'],
29099         'top' : [ 'b-t', [0,-2], 'bottom']
29100     }
29101     
29102 });
29103
29104
29105 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29106     
29107     
29108     bindEl : false,
29109     
29110     delay : null, // can be { show : 300 , hide: 500}
29111     
29112     timeout : null,
29113     
29114     hoverState : null, //???
29115     
29116     placement : 'bottom', 
29117     
29118     alignment : false,
29119     
29120     getAutoCreate : function(){
29121     
29122         var cfg = {
29123            cls : 'tooltip',   
29124            role : 'tooltip',
29125            cn : [
29126                 {
29127                     cls : 'tooltip-arrow arrow'
29128                 },
29129                 {
29130                     cls : 'tooltip-inner'
29131                 }
29132            ]
29133         };
29134         
29135         return cfg;
29136     },
29137     bind : function(el)
29138     {
29139         this.bindEl = el;
29140     },
29141     
29142     initEvents : function()
29143     {
29144         this.arrowEl = this.el.select('.arrow', true).first();
29145         this.innerEl = this.el.select('.tooltip-inner', true).first();
29146     },
29147     
29148     enter : function () {
29149        
29150         if (this.timeout != null) {
29151             clearTimeout(this.timeout);
29152         }
29153         
29154         this.hoverState = 'in';
29155          //Roo.log("enter - show");
29156         if (!this.delay || !this.delay.show) {
29157             this.show();
29158             return;
29159         }
29160         var _t = this;
29161         this.timeout = setTimeout(function () {
29162             if (_t.hoverState == 'in') {
29163                 _t.show();
29164             }
29165         }, this.delay.show);
29166     },
29167     leave : function()
29168     {
29169         clearTimeout(this.timeout);
29170     
29171         this.hoverState = 'out';
29172          if (!this.delay || !this.delay.hide) {
29173             this.hide();
29174             return;
29175         }
29176        
29177         var _t = this;
29178         this.timeout = setTimeout(function () {
29179             //Roo.log("leave - timeout");
29180             
29181             if (_t.hoverState == 'out') {
29182                 _t.hide();
29183                 Roo.bootstrap.Tooltip.currentEl = false;
29184             }
29185         }, delay);
29186     },
29187     
29188     show : function (msg)
29189     {
29190         if (!this.el) {
29191             this.render(document.body);
29192         }
29193         // set content.
29194         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29195         
29196         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29197         
29198         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29199         
29200         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29201                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29202         
29203         var placement = typeof this.placement == 'function' ?
29204             this.placement.call(this, this.el, on_el) :
29205             this.placement;
29206             
29207         var autoToken = /\s?auto?\s?/i;
29208         var autoPlace = autoToken.test(placement);
29209         if (autoPlace) {
29210             placement = placement.replace(autoToken, '') || 'top';
29211         }
29212         
29213         //this.el.detach()
29214         //this.el.setXY([0,0]);
29215         this.el.show();
29216         //this.el.dom.style.display='block';
29217         
29218         //this.el.appendTo(on_el);
29219         
29220         var p = this.getPosition();
29221         var box = this.el.getBox();
29222         
29223         if (autoPlace) {
29224             // fixme..
29225         }
29226         
29227         var align = this.alignment[placement];
29228         
29229         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29230         
29231         if(placement == 'top' || placement == 'bottom'){
29232             if(xy[0] < 0){
29233                 placement = 'right';
29234             }
29235             
29236             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29237                 placement = 'left';
29238             }
29239             
29240             var scroll = Roo.select('body', true).first().getScroll();
29241             
29242             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29243                 placement = 'top';
29244             }
29245             
29246             align = this.alignment[placement];
29247             
29248             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29249             
29250         }
29251         
29252         this.el.alignTo(this.bindEl, align[0],align[1]);
29253         //var arrow = this.el.select('.arrow',true).first();
29254         //arrow.set(align[2], 
29255         
29256         this.el.addClass(placement);
29257         this.el.addClass("bs-tooltip-"+ placement);
29258         
29259         this.el.addClass('in fade show');
29260         
29261         this.hoverState = null;
29262         
29263         if (this.el.hasClass('fade')) {
29264             // fade it?
29265         }
29266         
29267         
29268         
29269         
29270         
29271     },
29272     hide : function()
29273     {
29274          
29275         if (!this.el) {
29276             return;
29277         }
29278         //this.el.setXY([0,0]);
29279         this.el.removeClass(['show', 'in']);
29280         //this.el.hide();
29281         
29282     }
29283     
29284 });
29285  
29286
29287  /*
29288  * - LGPL
29289  *
29290  * Location Picker
29291  * 
29292  */
29293
29294 /**
29295  * @class Roo.bootstrap.LocationPicker
29296  * @extends Roo.bootstrap.Component
29297  * Bootstrap LocationPicker class
29298  * @cfg {Number} latitude Position when init default 0
29299  * @cfg {Number} longitude Position when init default 0
29300  * @cfg {Number} zoom default 15
29301  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29302  * @cfg {Boolean} mapTypeControl default false
29303  * @cfg {Boolean} disableDoubleClickZoom default false
29304  * @cfg {Boolean} scrollwheel default true
29305  * @cfg {Boolean} streetViewControl default false
29306  * @cfg {Number} radius default 0
29307  * @cfg {String} locationName
29308  * @cfg {Boolean} draggable default true
29309  * @cfg {Boolean} enableAutocomplete default false
29310  * @cfg {Boolean} enableReverseGeocode default true
29311  * @cfg {String} markerTitle
29312  * 
29313  * @constructor
29314  * Create a new LocationPicker
29315  * @param {Object} config The config object
29316  */
29317
29318
29319 Roo.bootstrap.LocationPicker = function(config){
29320     
29321     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29322     
29323     this.addEvents({
29324         /**
29325          * @event initial
29326          * Fires when the picker initialized.
29327          * @param {Roo.bootstrap.LocationPicker} this
29328          * @param {Google Location} location
29329          */
29330         initial : true,
29331         /**
29332          * @event positionchanged
29333          * Fires when the picker position changed.
29334          * @param {Roo.bootstrap.LocationPicker} this
29335          * @param {Google Location} location
29336          */
29337         positionchanged : true,
29338         /**
29339          * @event resize
29340          * Fires when the map resize.
29341          * @param {Roo.bootstrap.LocationPicker} this
29342          */
29343         resize : true,
29344         /**
29345          * @event show
29346          * Fires when the map show.
29347          * @param {Roo.bootstrap.LocationPicker} this
29348          */
29349         show : true,
29350         /**
29351          * @event hide
29352          * Fires when the map hide.
29353          * @param {Roo.bootstrap.LocationPicker} this
29354          */
29355         hide : true,
29356         /**
29357          * @event mapClick
29358          * Fires when click the map.
29359          * @param {Roo.bootstrap.LocationPicker} this
29360          * @param {Map event} e
29361          */
29362         mapClick : true,
29363         /**
29364          * @event mapRightClick
29365          * Fires when right click the map.
29366          * @param {Roo.bootstrap.LocationPicker} this
29367          * @param {Map event} e
29368          */
29369         mapRightClick : true,
29370         /**
29371          * @event markerClick
29372          * Fires when click the marker.
29373          * @param {Roo.bootstrap.LocationPicker} this
29374          * @param {Map event} e
29375          */
29376         markerClick : true,
29377         /**
29378          * @event markerRightClick
29379          * Fires when right click the marker.
29380          * @param {Roo.bootstrap.LocationPicker} this
29381          * @param {Map event} e
29382          */
29383         markerRightClick : true,
29384         /**
29385          * @event OverlayViewDraw
29386          * Fires when OverlayView Draw
29387          * @param {Roo.bootstrap.LocationPicker} this
29388          */
29389         OverlayViewDraw : true,
29390         /**
29391          * @event OverlayViewOnAdd
29392          * Fires when OverlayView Draw
29393          * @param {Roo.bootstrap.LocationPicker} this
29394          */
29395         OverlayViewOnAdd : true,
29396         /**
29397          * @event OverlayViewOnRemove
29398          * Fires when OverlayView Draw
29399          * @param {Roo.bootstrap.LocationPicker} this
29400          */
29401         OverlayViewOnRemove : true,
29402         /**
29403          * @event OverlayViewShow
29404          * Fires when OverlayView Draw
29405          * @param {Roo.bootstrap.LocationPicker} this
29406          * @param {Pixel} cpx
29407          */
29408         OverlayViewShow : true,
29409         /**
29410          * @event OverlayViewHide
29411          * Fires when OverlayView Draw
29412          * @param {Roo.bootstrap.LocationPicker} this
29413          */
29414         OverlayViewHide : true,
29415         /**
29416          * @event loadexception
29417          * Fires when load google lib failed.
29418          * @param {Roo.bootstrap.LocationPicker} this
29419          */
29420         loadexception : true
29421     });
29422         
29423 };
29424
29425 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29426     
29427     gMapContext: false,
29428     
29429     latitude: 0,
29430     longitude: 0,
29431     zoom: 15,
29432     mapTypeId: false,
29433     mapTypeControl: false,
29434     disableDoubleClickZoom: false,
29435     scrollwheel: true,
29436     streetViewControl: false,
29437     radius: 0,
29438     locationName: '',
29439     draggable: true,
29440     enableAutocomplete: false,
29441     enableReverseGeocode: true,
29442     markerTitle: '',
29443     
29444     getAutoCreate: function()
29445     {
29446
29447         var cfg = {
29448             tag: 'div',
29449             cls: 'roo-location-picker'
29450         };
29451         
29452         return cfg
29453     },
29454     
29455     initEvents: function(ct, position)
29456     {       
29457         if(!this.el.getWidth() || this.isApplied()){
29458             return;
29459         }
29460         
29461         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29462         
29463         this.initial();
29464     },
29465     
29466     initial: function()
29467     {
29468         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29469             this.fireEvent('loadexception', this);
29470             return;
29471         }
29472         
29473         if(!this.mapTypeId){
29474             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29475         }
29476         
29477         this.gMapContext = this.GMapContext();
29478         
29479         this.initOverlayView();
29480         
29481         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29482         
29483         var _this = this;
29484                 
29485         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29486             _this.setPosition(_this.gMapContext.marker.position);
29487         });
29488         
29489         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29490             _this.fireEvent('mapClick', this, event);
29491             
29492         });
29493
29494         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29495             _this.fireEvent('mapRightClick', this, event);
29496             
29497         });
29498         
29499         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29500             _this.fireEvent('markerClick', this, event);
29501             
29502         });
29503
29504         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29505             _this.fireEvent('markerRightClick', this, event);
29506             
29507         });
29508         
29509         this.setPosition(this.gMapContext.location);
29510         
29511         this.fireEvent('initial', this, this.gMapContext.location);
29512     },
29513     
29514     initOverlayView: function()
29515     {
29516         var _this = this;
29517         
29518         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29519             
29520             draw: function()
29521             {
29522                 _this.fireEvent('OverlayViewDraw', _this);
29523             },
29524             
29525             onAdd: function()
29526             {
29527                 _this.fireEvent('OverlayViewOnAdd', _this);
29528             },
29529             
29530             onRemove: function()
29531             {
29532                 _this.fireEvent('OverlayViewOnRemove', _this);
29533             },
29534             
29535             show: function(cpx)
29536             {
29537                 _this.fireEvent('OverlayViewShow', _this, cpx);
29538             },
29539             
29540             hide: function()
29541             {
29542                 _this.fireEvent('OverlayViewHide', _this);
29543             }
29544             
29545         });
29546     },
29547     
29548     fromLatLngToContainerPixel: function(event)
29549     {
29550         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29551     },
29552     
29553     isApplied: function() 
29554     {
29555         return this.getGmapContext() == false ? false : true;
29556     },
29557     
29558     getGmapContext: function() 
29559     {
29560         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29561     },
29562     
29563     GMapContext: function() 
29564     {
29565         var position = new google.maps.LatLng(this.latitude, this.longitude);
29566         
29567         var _map = new google.maps.Map(this.el.dom, {
29568             center: position,
29569             zoom: this.zoom,
29570             mapTypeId: this.mapTypeId,
29571             mapTypeControl: this.mapTypeControl,
29572             disableDoubleClickZoom: this.disableDoubleClickZoom,
29573             scrollwheel: this.scrollwheel,
29574             streetViewControl: this.streetViewControl,
29575             locationName: this.locationName,
29576             draggable: this.draggable,
29577             enableAutocomplete: this.enableAutocomplete,
29578             enableReverseGeocode: this.enableReverseGeocode
29579         });
29580         
29581         var _marker = new google.maps.Marker({
29582             position: position,
29583             map: _map,
29584             title: this.markerTitle,
29585             draggable: this.draggable
29586         });
29587         
29588         return {
29589             map: _map,
29590             marker: _marker,
29591             circle: null,
29592             location: position,
29593             radius: this.radius,
29594             locationName: this.locationName,
29595             addressComponents: {
29596                 formatted_address: null,
29597                 addressLine1: null,
29598                 addressLine2: null,
29599                 streetName: null,
29600                 streetNumber: null,
29601                 city: null,
29602                 district: null,
29603                 state: null,
29604                 stateOrProvince: null
29605             },
29606             settings: this,
29607             domContainer: this.el.dom,
29608             geodecoder: new google.maps.Geocoder()
29609         };
29610     },
29611     
29612     drawCircle: function(center, radius, options) 
29613     {
29614         if (this.gMapContext.circle != null) {
29615             this.gMapContext.circle.setMap(null);
29616         }
29617         if (radius > 0) {
29618             radius *= 1;
29619             options = Roo.apply({}, options, {
29620                 strokeColor: "#0000FF",
29621                 strokeOpacity: .35,
29622                 strokeWeight: 2,
29623                 fillColor: "#0000FF",
29624                 fillOpacity: .2
29625             });
29626             
29627             options.map = this.gMapContext.map;
29628             options.radius = radius;
29629             options.center = center;
29630             this.gMapContext.circle = new google.maps.Circle(options);
29631             return this.gMapContext.circle;
29632         }
29633         
29634         return null;
29635     },
29636     
29637     setPosition: function(location) 
29638     {
29639         this.gMapContext.location = location;
29640         this.gMapContext.marker.setPosition(location);
29641         this.gMapContext.map.panTo(location);
29642         this.drawCircle(location, this.gMapContext.radius, {});
29643         
29644         var _this = this;
29645         
29646         if (this.gMapContext.settings.enableReverseGeocode) {
29647             this.gMapContext.geodecoder.geocode({
29648                 latLng: this.gMapContext.location
29649             }, function(results, status) {
29650                 
29651                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29652                     _this.gMapContext.locationName = results[0].formatted_address;
29653                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29654                     
29655                     _this.fireEvent('positionchanged', this, location);
29656                 }
29657             });
29658             
29659             return;
29660         }
29661         
29662         this.fireEvent('positionchanged', this, location);
29663     },
29664     
29665     resize: function()
29666     {
29667         google.maps.event.trigger(this.gMapContext.map, "resize");
29668         
29669         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29670         
29671         this.fireEvent('resize', this);
29672     },
29673     
29674     setPositionByLatLng: function(latitude, longitude)
29675     {
29676         this.setPosition(new google.maps.LatLng(latitude, longitude));
29677     },
29678     
29679     getCurrentPosition: function() 
29680     {
29681         return {
29682             latitude: this.gMapContext.location.lat(),
29683             longitude: this.gMapContext.location.lng()
29684         };
29685     },
29686     
29687     getAddressName: function() 
29688     {
29689         return this.gMapContext.locationName;
29690     },
29691     
29692     getAddressComponents: function() 
29693     {
29694         return this.gMapContext.addressComponents;
29695     },
29696     
29697     address_component_from_google_geocode: function(address_components) 
29698     {
29699         var result = {};
29700         
29701         for (var i = 0; i < address_components.length; i++) {
29702             var component = address_components[i];
29703             if (component.types.indexOf("postal_code") >= 0) {
29704                 result.postalCode = component.short_name;
29705             } else if (component.types.indexOf("street_number") >= 0) {
29706                 result.streetNumber = component.short_name;
29707             } else if (component.types.indexOf("route") >= 0) {
29708                 result.streetName = component.short_name;
29709             } else if (component.types.indexOf("neighborhood") >= 0) {
29710                 result.city = component.short_name;
29711             } else if (component.types.indexOf("locality") >= 0) {
29712                 result.city = component.short_name;
29713             } else if (component.types.indexOf("sublocality") >= 0) {
29714                 result.district = component.short_name;
29715             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29716                 result.stateOrProvince = component.short_name;
29717             } else if (component.types.indexOf("country") >= 0) {
29718                 result.country = component.short_name;
29719             }
29720         }
29721         
29722         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29723         result.addressLine2 = "";
29724         return result;
29725     },
29726     
29727     setZoomLevel: function(zoom)
29728     {
29729         this.gMapContext.map.setZoom(zoom);
29730     },
29731     
29732     show: function()
29733     {
29734         if(!this.el){
29735             return;
29736         }
29737         
29738         this.el.show();
29739         
29740         this.resize();
29741         
29742         this.fireEvent('show', this);
29743     },
29744     
29745     hide: function()
29746     {
29747         if(!this.el){
29748             return;
29749         }
29750         
29751         this.el.hide();
29752         
29753         this.fireEvent('hide', this);
29754     }
29755     
29756 });
29757
29758 Roo.apply(Roo.bootstrap.LocationPicker, {
29759     
29760     OverlayView : function(map, options)
29761     {
29762         options = options || {};
29763         
29764         this.setMap(map);
29765     }
29766     
29767     
29768 });/**
29769  * @class Roo.bootstrap.Alert
29770  * @extends Roo.bootstrap.Component
29771  * Bootstrap Alert class - shows an alert area box
29772  * eg
29773  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29774   Enter a valid email address
29775 </div>
29776  * @licence LGPL
29777  * @cfg {String} title The title of alert
29778  * @cfg {String} html The content of alert
29779  * @cfg {String} weight (  success | info | warning | danger )
29780  * @cfg {String} faicon font-awesomeicon
29781  * 
29782  * @constructor
29783  * Create a new alert
29784  * @param {Object} config The config object
29785  */
29786
29787
29788 Roo.bootstrap.Alert = function(config){
29789     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29790     
29791 };
29792
29793 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29794     
29795     title: '',
29796     html: '',
29797     weight: false,
29798     faicon: false,
29799     
29800     getAutoCreate : function()
29801     {
29802         
29803         var cfg = {
29804             tag : 'div',
29805             cls : 'alert',
29806             cn : [
29807                 {
29808                     tag : 'i',
29809                     cls : 'roo-alert-icon'
29810                     
29811                 },
29812                 {
29813                     tag : 'b',
29814                     cls : 'roo-alert-title',
29815                     html : this.title
29816                 },
29817                 {
29818                     tag : 'span',
29819                     cls : 'roo-alert-text',
29820                     html : this.html
29821                 }
29822             ]
29823         };
29824         
29825         if(this.faicon){
29826             cfg.cn[0].cls += ' fa ' + this.faicon;
29827         }
29828         
29829         if(this.weight){
29830             cfg.cls += ' alert-' + this.weight;
29831         }
29832         
29833         return cfg;
29834     },
29835     
29836     initEvents: function() 
29837     {
29838         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29839     },
29840     
29841     setTitle : function(str)
29842     {
29843         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29844     },
29845     
29846     setText : function(str)
29847     {
29848         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29849     },
29850     
29851     setWeight : function(weight)
29852     {
29853         if(this.weight){
29854             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29855         }
29856         
29857         this.weight = weight;
29858         
29859         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29860     },
29861     
29862     setIcon : function(icon)
29863     {
29864         if(this.faicon){
29865             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29866         }
29867         
29868         this.faicon = icon;
29869         
29870         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29871     },
29872     
29873     hide: function() 
29874     {
29875         this.el.hide();   
29876     },
29877     
29878     show: function() 
29879     {  
29880         this.el.show();   
29881     }
29882     
29883 });
29884
29885  
29886 /*
29887 * Licence: LGPL
29888 */
29889
29890 /**
29891  * @class Roo.bootstrap.UploadCropbox
29892  * @extends Roo.bootstrap.Component
29893  * Bootstrap UploadCropbox class
29894  * @cfg {String} emptyText show when image has been loaded
29895  * @cfg {String} rotateNotify show when image too small to rotate
29896  * @cfg {Number} errorTimeout default 3000
29897  * @cfg {Number} minWidth default 300
29898  * @cfg {Number} minHeight default 300
29899  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29900  * @cfg {Boolean} isDocument (true|false) default false
29901  * @cfg {String} url action url
29902  * @cfg {String} paramName default 'imageUpload'
29903  * @cfg {String} method default POST
29904  * @cfg {Boolean} loadMask (true|false) default true
29905  * @cfg {Boolean} loadingText default 'Loading...'
29906  * 
29907  * @constructor
29908  * Create a new UploadCropbox
29909  * @param {Object} config The config object
29910  */
29911
29912 Roo.bootstrap.UploadCropbox = function(config){
29913     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29914     
29915     this.addEvents({
29916         /**
29917          * @event beforeselectfile
29918          * Fire before select file
29919          * @param {Roo.bootstrap.UploadCropbox} this
29920          */
29921         "beforeselectfile" : true,
29922         /**
29923          * @event initial
29924          * Fire after initEvent
29925          * @param {Roo.bootstrap.UploadCropbox} this
29926          */
29927         "initial" : true,
29928         /**
29929          * @event crop
29930          * Fire after initEvent
29931          * @param {Roo.bootstrap.UploadCropbox} this
29932          * @param {String} data
29933          */
29934         "crop" : true,
29935         /**
29936          * @event prepare
29937          * Fire when preparing the file data
29938          * @param {Roo.bootstrap.UploadCropbox} this
29939          * @param {Object} file
29940          */
29941         "prepare" : true,
29942         /**
29943          * @event exception
29944          * Fire when get exception
29945          * @param {Roo.bootstrap.UploadCropbox} this
29946          * @param {XMLHttpRequest} xhr
29947          */
29948         "exception" : true,
29949         /**
29950          * @event beforeloadcanvas
29951          * Fire before load the canvas
29952          * @param {Roo.bootstrap.UploadCropbox} this
29953          * @param {String} src
29954          */
29955         "beforeloadcanvas" : true,
29956         /**
29957          * @event trash
29958          * Fire when trash image
29959          * @param {Roo.bootstrap.UploadCropbox} this
29960          */
29961         "trash" : true,
29962         /**
29963          * @event download
29964          * Fire when download the image
29965          * @param {Roo.bootstrap.UploadCropbox} this
29966          */
29967         "download" : true,
29968         /**
29969          * @event footerbuttonclick
29970          * Fire when footerbuttonclick
29971          * @param {Roo.bootstrap.UploadCropbox} this
29972          * @param {String} type
29973          */
29974         "footerbuttonclick" : true,
29975         /**
29976          * @event resize
29977          * Fire when resize
29978          * @param {Roo.bootstrap.UploadCropbox} this
29979          */
29980         "resize" : true,
29981         /**
29982          * @event rotate
29983          * Fire when rotate the image
29984          * @param {Roo.bootstrap.UploadCropbox} this
29985          * @param {String} pos
29986          */
29987         "rotate" : true,
29988         /**
29989          * @event inspect
29990          * Fire when inspect the file
29991          * @param {Roo.bootstrap.UploadCropbox} this
29992          * @param {Object} file
29993          */
29994         "inspect" : true,
29995         /**
29996          * @event upload
29997          * Fire when xhr upload the file
29998          * @param {Roo.bootstrap.UploadCropbox} this
29999          * @param {Object} data
30000          */
30001         "upload" : true,
30002         /**
30003          * @event arrange
30004          * Fire when arrange the file data
30005          * @param {Roo.bootstrap.UploadCropbox} this
30006          * @param {Object} formData
30007          */
30008         "arrange" : true
30009     });
30010     
30011     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30012 };
30013
30014 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30015     
30016     emptyText : 'Click to upload image',
30017     rotateNotify : 'Image is too small to rotate',
30018     errorTimeout : 3000,
30019     scale : 0,
30020     baseScale : 1,
30021     rotate : 0,
30022     dragable : false,
30023     pinching : false,
30024     mouseX : 0,
30025     mouseY : 0,
30026     cropData : false,
30027     minWidth : 300,
30028     minHeight : 300,
30029     file : false,
30030     exif : {},
30031     baseRotate : 1,
30032     cropType : 'image/jpeg',
30033     buttons : false,
30034     canvasLoaded : false,
30035     isDocument : false,
30036     method : 'POST',
30037     paramName : 'imageUpload',
30038     loadMask : true,
30039     loadingText : 'Loading...',
30040     maskEl : false,
30041     
30042     getAutoCreate : function()
30043     {
30044         var cfg = {
30045             tag : 'div',
30046             cls : 'roo-upload-cropbox',
30047             cn : [
30048                 {
30049                     tag : 'input',
30050                     cls : 'roo-upload-cropbox-selector',
30051                     type : 'file'
30052                 },
30053                 {
30054                     tag : 'div',
30055                     cls : 'roo-upload-cropbox-body',
30056                     style : 'cursor:pointer',
30057                     cn : [
30058                         {
30059                             tag : 'div',
30060                             cls : 'roo-upload-cropbox-preview'
30061                         },
30062                         {
30063                             tag : 'div',
30064                             cls : 'roo-upload-cropbox-thumb'
30065                         },
30066                         {
30067                             tag : 'div',
30068                             cls : 'roo-upload-cropbox-empty-notify',
30069                             html : this.emptyText
30070                         },
30071                         {
30072                             tag : 'div',
30073                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30074                             html : this.rotateNotify
30075                         }
30076                     ]
30077                 },
30078                 {
30079                     tag : 'div',
30080                     cls : 'roo-upload-cropbox-footer',
30081                     cn : {
30082                         tag : 'div',
30083                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30084                         cn : []
30085                     }
30086                 }
30087             ]
30088         };
30089         
30090         return cfg;
30091     },
30092     
30093     onRender : function(ct, position)
30094     {
30095         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30096         
30097         if (this.buttons.length) {
30098             
30099             Roo.each(this.buttons, function(bb) {
30100                 
30101                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30102                 
30103                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30104                 
30105             }, this);
30106         }
30107         
30108         if(this.loadMask){
30109             this.maskEl = this.el;
30110         }
30111     },
30112     
30113     initEvents : function()
30114     {
30115         this.urlAPI = (window.createObjectURL && window) || 
30116                                 (window.URL && URL.revokeObjectURL && URL) || 
30117                                 (window.webkitURL && webkitURL);
30118                         
30119         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30120         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30121         
30122         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30123         this.selectorEl.hide();
30124         
30125         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30126         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30127         
30128         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30129         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30130         this.thumbEl.hide();
30131         
30132         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30133         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30134         
30135         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30136         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30137         this.errorEl.hide();
30138         
30139         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30140         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30141         this.footerEl.hide();
30142         
30143         this.setThumbBoxSize();
30144         
30145         this.bind();
30146         
30147         this.resize();
30148         
30149         this.fireEvent('initial', this);
30150     },
30151
30152     bind : function()
30153     {
30154         var _this = this;
30155         
30156         window.addEventListener("resize", function() { _this.resize(); } );
30157         
30158         this.bodyEl.on('click', this.beforeSelectFile, this);
30159         
30160         if(Roo.isTouch){
30161             this.bodyEl.on('touchstart', this.onTouchStart, this);
30162             this.bodyEl.on('touchmove', this.onTouchMove, this);
30163             this.bodyEl.on('touchend', this.onTouchEnd, this);
30164         }
30165         
30166         if(!Roo.isTouch){
30167             this.bodyEl.on('mousedown', this.onMouseDown, this);
30168             this.bodyEl.on('mousemove', this.onMouseMove, this);
30169             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30170             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30171             Roo.get(document).on('mouseup', this.onMouseUp, this);
30172         }
30173         
30174         this.selectorEl.on('change', this.onFileSelected, this);
30175     },
30176     
30177     reset : function()
30178     {    
30179         this.scale = 0;
30180         this.baseScale = 1;
30181         this.rotate = 0;
30182         this.baseRotate = 1;
30183         this.dragable = false;
30184         this.pinching = false;
30185         this.mouseX = 0;
30186         this.mouseY = 0;
30187         this.cropData = false;
30188         this.notifyEl.dom.innerHTML = this.emptyText;
30189         
30190         this.selectorEl.dom.value = '';
30191         
30192     },
30193     
30194     resize : function()
30195     {
30196         if(this.fireEvent('resize', this) != false){
30197             this.setThumbBoxPosition();
30198             this.setCanvasPosition();
30199         }
30200     },
30201     
30202     onFooterButtonClick : function(e, el, o, type)
30203     {
30204         switch (type) {
30205             case 'rotate-left' :
30206                 this.onRotateLeft(e);
30207                 break;
30208             case 'rotate-right' :
30209                 this.onRotateRight(e);
30210                 break;
30211             case 'picture' :
30212                 this.beforeSelectFile(e);
30213                 break;
30214             case 'trash' :
30215                 this.trash(e);
30216                 break;
30217             case 'crop' :
30218                 this.crop(e);
30219                 break;
30220             case 'download' :
30221                 this.download(e);
30222                 break;
30223             default :
30224                 break;
30225         }
30226         
30227         this.fireEvent('footerbuttonclick', this, type);
30228     },
30229     
30230     beforeSelectFile : function(e)
30231     {
30232         e.preventDefault();
30233         
30234         if(this.fireEvent('beforeselectfile', this) != false){
30235             this.selectorEl.dom.click();
30236         }
30237     },
30238     
30239     onFileSelected : function(e)
30240     {
30241         e.preventDefault();
30242         
30243         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30244             return;
30245         }
30246         
30247         var file = this.selectorEl.dom.files[0];
30248         
30249         if(this.fireEvent('inspect', this, file) != false){
30250             this.prepare(file);
30251         }
30252         
30253     },
30254     
30255     trash : function(e)
30256     {
30257         this.fireEvent('trash', this);
30258     },
30259     
30260     download : function(e)
30261     {
30262         this.fireEvent('download', this);
30263     },
30264     
30265     loadCanvas : function(src)
30266     {   
30267         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30268             
30269             this.reset();
30270             
30271             this.imageEl = document.createElement('img');
30272             
30273             var _this = this;
30274             
30275             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30276             
30277             this.imageEl.src = src;
30278         }
30279     },
30280     
30281     onLoadCanvas : function()
30282     {   
30283         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30284         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30285         
30286         this.bodyEl.un('click', this.beforeSelectFile, this);
30287         
30288         this.notifyEl.hide();
30289         this.thumbEl.show();
30290         this.footerEl.show();
30291         
30292         this.baseRotateLevel();
30293         
30294         if(this.isDocument){
30295             this.setThumbBoxSize();
30296         }
30297         
30298         this.setThumbBoxPosition();
30299         
30300         this.baseScaleLevel();
30301         
30302         this.draw();
30303         
30304         this.resize();
30305         
30306         this.canvasLoaded = true;
30307         
30308         if(this.loadMask){
30309             this.maskEl.unmask();
30310         }
30311         
30312     },
30313     
30314     setCanvasPosition : function()
30315     {   
30316         if(!this.canvasEl){
30317             return;
30318         }
30319         
30320         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30321         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30322         
30323         this.previewEl.setLeft(pw);
30324         this.previewEl.setTop(ph);
30325         
30326     },
30327     
30328     onMouseDown : function(e)
30329     {   
30330         e.stopEvent();
30331         
30332         this.dragable = true;
30333         this.pinching = false;
30334         
30335         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30336             this.dragable = false;
30337             return;
30338         }
30339         
30340         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30341         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30342         
30343     },
30344     
30345     onMouseMove : function(e)
30346     {   
30347         e.stopEvent();
30348         
30349         if(!this.canvasLoaded){
30350             return;
30351         }
30352         
30353         if (!this.dragable){
30354             return;
30355         }
30356         
30357         var minX = Math.ceil(this.thumbEl.getLeft(true));
30358         var minY = Math.ceil(this.thumbEl.getTop(true));
30359         
30360         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30361         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30362         
30363         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30364         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30365         
30366         x = x - this.mouseX;
30367         y = y - this.mouseY;
30368         
30369         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30370         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30371         
30372         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30373         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30374         
30375         this.previewEl.setLeft(bgX);
30376         this.previewEl.setTop(bgY);
30377         
30378         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30379         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30380     },
30381     
30382     onMouseUp : function(e)
30383     {   
30384         e.stopEvent();
30385         
30386         this.dragable = false;
30387     },
30388     
30389     onMouseWheel : function(e)
30390     {   
30391         e.stopEvent();
30392         
30393         this.startScale = this.scale;
30394         
30395         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30396         
30397         if(!this.zoomable()){
30398             this.scale = this.startScale;
30399             return;
30400         }
30401         
30402         this.draw();
30403         
30404         return;
30405     },
30406     
30407     zoomable : function()
30408     {
30409         var minScale = this.thumbEl.getWidth() / this.minWidth;
30410         
30411         if(this.minWidth < this.minHeight){
30412             minScale = this.thumbEl.getHeight() / this.minHeight;
30413         }
30414         
30415         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30416         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30417         
30418         if(
30419                 this.isDocument &&
30420                 (this.rotate == 0 || this.rotate == 180) && 
30421                 (
30422                     width > this.imageEl.OriginWidth || 
30423                     height > this.imageEl.OriginHeight ||
30424                     (width < this.minWidth && height < this.minHeight)
30425                 )
30426         ){
30427             return false;
30428         }
30429         
30430         if(
30431                 this.isDocument &&
30432                 (this.rotate == 90 || this.rotate == 270) && 
30433                 (
30434                     width > this.imageEl.OriginWidth || 
30435                     height > this.imageEl.OriginHeight ||
30436                     (width < this.minHeight && height < this.minWidth)
30437                 )
30438         ){
30439             return false;
30440         }
30441         
30442         if(
30443                 !this.isDocument &&
30444                 (this.rotate == 0 || this.rotate == 180) && 
30445                 (
30446                     width < this.minWidth || 
30447                     width > this.imageEl.OriginWidth || 
30448                     height < this.minHeight || 
30449                     height > this.imageEl.OriginHeight
30450                 )
30451         ){
30452             return false;
30453         }
30454         
30455         if(
30456                 !this.isDocument &&
30457                 (this.rotate == 90 || this.rotate == 270) && 
30458                 (
30459                     width < this.minHeight || 
30460                     width > this.imageEl.OriginWidth || 
30461                     height < this.minWidth || 
30462                     height > this.imageEl.OriginHeight
30463                 )
30464         ){
30465             return false;
30466         }
30467         
30468         return true;
30469         
30470     },
30471     
30472     onRotateLeft : function(e)
30473     {   
30474         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30475             
30476             var minScale = this.thumbEl.getWidth() / this.minWidth;
30477             
30478             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30479             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30480             
30481             this.startScale = this.scale;
30482             
30483             while (this.getScaleLevel() < minScale){
30484             
30485                 this.scale = this.scale + 1;
30486                 
30487                 if(!this.zoomable()){
30488                     break;
30489                 }
30490                 
30491                 if(
30492                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30493                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30494                 ){
30495                     continue;
30496                 }
30497                 
30498                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30499
30500                 this.draw();
30501                 
30502                 return;
30503             }
30504             
30505             this.scale = this.startScale;
30506             
30507             this.onRotateFail();
30508             
30509             return false;
30510         }
30511         
30512         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30513
30514         if(this.isDocument){
30515             this.setThumbBoxSize();
30516             this.setThumbBoxPosition();
30517             this.setCanvasPosition();
30518         }
30519         
30520         this.draw();
30521         
30522         this.fireEvent('rotate', this, 'left');
30523         
30524     },
30525     
30526     onRotateRight : function(e)
30527     {
30528         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30529             
30530             var minScale = this.thumbEl.getWidth() / this.minWidth;
30531         
30532             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30533             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30534             
30535             this.startScale = this.scale;
30536             
30537             while (this.getScaleLevel() < minScale){
30538             
30539                 this.scale = this.scale + 1;
30540                 
30541                 if(!this.zoomable()){
30542                     break;
30543                 }
30544                 
30545                 if(
30546                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30547                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30548                 ){
30549                     continue;
30550                 }
30551                 
30552                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30553
30554                 this.draw();
30555                 
30556                 return;
30557             }
30558             
30559             this.scale = this.startScale;
30560             
30561             this.onRotateFail();
30562             
30563             return false;
30564         }
30565         
30566         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30567
30568         if(this.isDocument){
30569             this.setThumbBoxSize();
30570             this.setThumbBoxPosition();
30571             this.setCanvasPosition();
30572         }
30573         
30574         this.draw();
30575         
30576         this.fireEvent('rotate', this, 'right');
30577     },
30578     
30579     onRotateFail : function()
30580     {
30581         this.errorEl.show(true);
30582         
30583         var _this = this;
30584         
30585         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30586     },
30587     
30588     draw : function()
30589     {
30590         this.previewEl.dom.innerHTML = '';
30591         
30592         var canvasEl = document.createElement("canvas");
30593         
30594         var contextEl = canvasEl.getContext("2d");
30595         
30596         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30597         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30598         var center = this.imageEl.OriginWidth / 2;
30599         
30600         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30601             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30602             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30603             center = this.imageEl.OriginHeight / 2;
30604         }
30605         
30606         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30607         
30608         contextEl.translate(center, center);
30609         contextEl.rotate(this.rotate * Math.PI / 180);
30610
30611         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30612         
30613         this.canvasEl = document.createElement("canvas");
30614         
30615         this.contextEl = this.canvasEl.getContext("2d");
30616         
30617         switch (this.rotate) {
30618             case 0 :
30619                 
30620                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30621                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30622                 
30623                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30624                 
30625                 break;
30626             case 90 : 
30627                 
30628                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30629                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30630                 
30631                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30632                     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);
30633                     break;
30634                 }
30635                 
30636                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30637                 
30638                 break;
30639             case 180 :
30640                 
30641                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30642                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30643                 
30644                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30645                     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);
30646                     break;
30647                 }
30648                 
30649                 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);
30650                 
30651                 break;
30652             case 270 :
30653                 
30654                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30655                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30656         
30657                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30658                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30659                     break;
30660                 }
30661                 
30662                 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);
30663                 
30664                 break;
30665             default : 
30666                 break;
30667         }
30668         
30669         this.previewEl.appendChild(this.canvasEl);
30670         
30671         this.setCanvasPosition();
30672     },
30673     
30674     crop : function()
30675     {
30676         if(!this.canvasLoaded){
30677             return;
30678         }
30679         
30680         var imageCanvas = document.createElement("canvas");
30681         
30682         var imageContext = imageCanvas.getContext("2d");
30683         
30684         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30685         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30686         
30687         var center = imageCanvas.width / 2;
30688         
30689         imageContext.translate(center, center);
30690         
30691         imageContext.rotate(this.rotate * Math.PI / 180);
30692         
30693         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30694         
30695         var canvas = document.createElement("canvas");
30696         
30697         var context = canvas.getContext("2d");
30698                 
30699         canvas.width = this.minWidth;
30700         canvas.height = this.minHeight;
30701
30702         switch (this.rotate) {
30703             case 0 :
30704                 
30705                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30706                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30707                 
30708                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30709                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30710                 
30711                 var targetWidth = this.minWidth - 2 * x;
30712                 var targetHeight = this.minHeight - 2 * y;
30713                 
30714                 var scale = 1;
30715                 
30716                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30717                     scale = targetWidth / width;
30718                 }
30719                 
30720                 if(x > 0 && y == 0){
30721                     scale = targetHeight / height;
30722                 }
30723                 
30724                 if(x > 0 && y > 0){
30725                     scale = targetWidth / width;
30726                     
30727                     if(width < height){
30728                         scale = targetHeight / height;
30729                     }
30730                 }
30731                 
30732                 context.scale(scale, scale);
30733                 
30734                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30735                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30736
30737                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30738                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30739
30740                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30741                 
30742                 break;
30743             case 90 : 
30744                 
30745                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30746                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30747                 
30748                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30749                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30750                 
30751                 var targetWidth = this.minWidth - 2 * x;
30752                 var targetHeight = this.minHeight - 2 * y;
30753                 
30754                 var scale = 1;
30755                 
30756                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30757                     scale = targetWidth / width;
30758                 }
30759                 
30760                 if(x > 0 && y == 0){
30761                     scale = targetHeight / height;
30762                 }
30763                 
30764                 if(x > 0 && y > 0){
30765                     scale = targetWidth / width;
30766                     
30767                     if(width < height){
30768                         scale = targetHeight / height;
30769                     }
30770                 }
30771                 
30772                 context.scale(scale, scale);
30773                 
30774                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30775                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30776
30777                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30778                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30779                 
30780                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30781                 
30782                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30783                 
30784                 break;
30785             case 180 :
30786                 
30787                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30788                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30789                 
30790                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30791                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30792                 
30793                 var targetWidth = this.minWidth - 2 * x;
30794                 var targetHeight = this.minHeight - 2 * y;
30795                 
30796                 var scale = 1;
30797                 
30798                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30799                     scale = targetWidth / width;
30800                 }
30801                 
30802                 if(x > 0 && y == 0){
30803                     scale = targetHeight / height;
30804                 }
30805                 
30806                 if(x > 0 && y > 0){
30807                     scale = targetWidth / width;
30808                     
30809                     if(width < height){
30810                         scale = targetHeight / height;
30811                     }
30812                 }
30813                 
30814                 context.scale(scale, scale);
30815                 
30816                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30817                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30818
30819                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30820                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30821
30822                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30823                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30824                 
30825                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30826                 
30827                 break;
30828             case 270 :
30829                 
30830                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30831                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30832                 
30833                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30834                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30835                 
30836                 var targetWidth = this.minWidth - 2 * x;
30837                 var targetHeight = this.minHeight - 2 * y;
30838                 
30839                 var scale = 1;
30840                 
30841                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30842                     scale = targetWidth / width;
30843                 }
30844                 
30845                 if(x > 0 && y == 0){
30846                     scale = targetHeight / height;
30847                 }
30848                 
30849                 if(x > 0 && y > 0){
30850                     scale = targetWidth / width;
30851                     
30852                     if(width < height){
30853                         scale = targetHeight / height;
30854                     }
30855                 }
30856                 
30857                 context.scale(scale, scale);
30858                 
30859                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30860                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30861
30862                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30863                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30864                 
30865                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30866                 
30867                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30868                 
30869                 break;
30870             default : 
30871                 break;
30872         }
30873         
30874         this.cropData = canvas.toDataURL(this.cropType);
30875         
30876         if(this.fireEvent('crop', this, this.cropData) !== false){
30877             this.process(this.file, this.cropData);
30878         }
30879         
30880         return;
30881         
30882     },
30883     
30884     setThumbBoxSize : function()
30885     {
30886         var width, height;
30887         
30888         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30889             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30890             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30891             
30892             this.minWidth = width;
30893             this.minHeight = height;
30894             
30895             if(this.rotate == 90 || this.rotate == 270){
30896                 this.minWidth = height;
30897                 this.minHeight = width;
30898             }
30899         }
30900         
30901         height = 300;
30902         width = Math.ceil(this.minWidth * height / this.minHeight);
30903         
30904         if(this.minWidth > this.minHeight){
30905             width = 300;
30906             height = Math.ceil(this.minHeight * width / this.minWidth);
30907         }
30908         
30909         this.thumbEl.setStyle({
30910             width : width + 'px',
30911             height : height + 'px'
30912         });
30913
30914         return;
30915             
30916     },
30917     
30918     setThumbBoxPosition : function()
30919     {
30920         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30921         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30922         
30923         this.thumbEl.setLeft(x);
30924         this.thumbEl.setTop(y);
30925         
30926     },
30927     
30928     baseRotateLevel : function()
30929     {
30930         this.baseRotate = 1;
30931         
30932         if(
30933                 typeof(this.exif) != 'undefined' &&
30934                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30935                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30936         ){
30937             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30938         }
30939         
30940         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30941         
30942     },
30943     
30944     baseScaleLevel : function()
30945     {
30946         var width, height;
30947         
30948         if(this.isDocument){
30949             
30950             if(this.baseRotate == 6 || this.baseRotate == 8){
30951             
30952                 height = this.thumbEl.getHeight();
30953                 this.baseScale = height / this.imageEl.OriginWidth;
30954
30955                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30956                     width = this.thumbEl.getWidth();
30957                     this.baseScale = width / this.imageEl.OriginHeight;
30958                 }
30959
30960                 return;
30961             }
30962
30963             height = this.thumbEl.getHeight();
30964             this.baseScale = height / this.imageEl.OriginHeight;
30965
30966             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30967                 width = this.thumbEl.getWidth();
30968                 this.baseScale = width / this.imageEl.OriginWidth;
30969             }
30970
30971             return;
30972         }
30973         
30974         if(this.baseRotate == 6 || this.baseRotate == 8){
30975             
30976             width = this.thumbEl.getHeight();
30977             this.baseScale = width / this.imageEl.OriginHeight;
30978             
30979             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30980                 height = this.thumbEl.getWidth();
30981                 this.baseScale = height / this.imageEl.OriginHeight;
30982             }
30983             
30984             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30985                 height = this.thumbEl.getWidth();
30986                 this.baseScale = height / this.imageEl.OriginHeight;
30987                 
30988                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30989                     width = this.thumbEl.getHeight();
30990                     this.baseScale = width / this.imageEl.OriginWidth;
30991                 }
30992             }
30993             
30994             return;
30995         }
30996         
30997         width = this.thumbEl.getWidth();
30998         this.baseScale = width / this.imageEl.OriginWidth;
30999         
31000         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31001             height = this.thumbEl.getHeight();
31002             this.baseScale = height / this.imageEl.OriginHeight;
31003         }
31004         
31005         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31006             
31007             height = this.thumbEl.getHeight();
31008             this.baseScale = height / this.imageEl.OriginHeight;
31009             
31010             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31011                 width = this.thumbEl.getWidth();
31012                 this.baseScale = width / this.imageEl.OriginWidth;
31013             }
31014             
31015         }
31016         
31017         return;
31018     },
31019     
31020     getScaleLevel : function()
31021     {
31022         return this.baseScale * Math.pow(1.1, this.scale);
31023     },
31024     
31025     onTouchStart : function(e)
31026     {
31027         if(!this.canvasLoaded){
31028             this.beforeSelectFile(e);
31029             return;
31030         }
31031         
31032         var touches = e.browserEvent.touches;
31033         
31034         if(!touches){
31035             return;
31036         }
31037         
31038         if(touches.length == 1){
31039             this.onMouseDown(e);
31040             return;
31041         }
31042         
31043         if(touches.length != 2){
31044             return;
31045         }
31046         
31047         var coords = [];
31048         
31049         for(var i = 0, finger; finger = touches[i]; i++){
31050             coords.push(finger.pageX, finger.pageY);
31051         }
31052         
31053         var x = Math.pow(coords[0] - coords[2], 2);
31054         var y = Math.pow(coords[1] - coords[3], 2);
31055         
31056         this.startDistance = Math.sqrt(x + y);
31057         
31058         this.startScale = this.scale;
31059         
31060         this.pinching = true;
31061         this.dragable = false;
31062         
31063     },
31064     
31065     onTouchMove : function(e)
31066     {
31067         if(!this.pinching && !this.dragable){
31068             return;
31069         }
31070         
31071         var touches = e.browserEvent.touches;
31072         
31073         if(!touches){
31074             return;
31075         }
31076         
31077         if(this.dragable){
31078             this.onMouseMove(e);
31079             return;
31080         }
31081         
31082         var coords = [];
31083         
31084         for(var i = 0, finger; finger = touches[i]; i++){
31085             coords.push(finger.pageX, finger.pageY);
31086         }
31087         
31088         var x = Math.pow(coords[0] - coords[2], 2);
31089         var y = Math.pow(coords[1] - coords[3], 2);
31090         
31091         this.endDistance = Math.sqrt(x + y);
31092         
31093         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31094         
31095         if(!this.zoomable()){
31096             this.scale = this.startScale;
31097             return;
31098         }
31099         
31100         this.draw();
31101         
31102     },
31103     
31104     onTouchEnd : function(e)
31105     {
31106         this.pinching = false;
31107         this.dragable = false;
31108         
31109     },
31110     
31111     process : function(file, crop)
31112     {
31113         if(this.loadMask){
31114             this.maskEl.mask(this.loadingText);
31115         }
31116         
31117         this.xhr = new XMLHttpRequest();
31118         
31119         file.xhr = this.xhr;
31120
31121         this.xhr.open(this.method, this.url, true);
31122         
31123         var headers = {
31124             "Accept": "application/json",
31125             "Cache-Control": "no-cache",
31126             "X-Requested-With": "XMLHttpRequest"
31127         };
31128         
31129         for (var headerName in headers) {
31130             var headerValue = headers[headerName];
31131             if (headerValue) {
31132                 this.xhr.setRequestHeader(headerName, headerValue);
31133             }
31134         }
31135         
31136         var _this = this;
31137         
31138         this.xhr.onload = function()
31139         {
31140             _this.xhrOnLoad(_this.xhr);
31141         }
31142         
31143         this.xhr.onerror = function()
31144         {
31145             _this.xhrOnError(_this.xhr);
31146         }
31147         
31148         var formData = new FormData();
31149
31150         formData.append('returnHTML', 'NO');
31151         
31152         if(crop){
31153             formData.append('crop', crop);
31154         }
31155         
31156         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31157             formData.append(this.paramName, file, file.name);
31158         }
31159         
31160         if(typeof(file.filename) != 'undefined'){
31161             formData.append('filename', file.filename);
31162         }
31163         
31164         if(typeof(file.mimetype) != 'undefined'){
31165             formData.append('mimetype', file.mimetype);
31166         }
31167         
31168         if(this.fireEvent('arrange', this, formData) != false){
31169             this.xhr.send(formData);
31170         };
31171     },
31172     
31173     xhrOnLoad : function(xhr)
31174     {
31175         if(this.loadMask){
31176             this.maskEl.unmask();
31177         }
31178         
31179         if (xhr.readyState !== 4) {
31180             this.fireEvent('exception', this, xhr);
31181             return;
31182         }
31183
31184         var response = Roo.decode(xhr.responseText);
31185         
31186         if(!response.success){
31187             this.fireEvent('exception', this, xhr);
31188             return;
31189         }
31190         
31191         var response = Roo.decode(xhr.responseText);
31192         
31193         this.fireEvent('upload', this, response);
31194         
31195     },
31196     
31197     xhrOnError : function()
31198     {
31199         if(this.loadMask){
31200             this.maskEl.unmask();
31201         }
31202         
31203         Roo.log('xhr on error');
31204         
31205         var response = Roo.decode(xhr.responseText);
31206           
31207         Roo.log(response);
31208         
31209     },
31210     
31211     prepare : function(file)
31212     {   
31213         if(this.loadMask){
31214             this.maskEl.mask(this.loadingText);
31215         }
31216         
31217         this.file = false;
31218         this.exif = {};
31219         
31220         if(typeof(file) === 'string'){
31221             this.loadCanvas(file);
31222             return;
31223         }
31224         
31225         if(!file || !this.urlAPI){
31226             return;
31227         }
31228         
31229         this.file = file;
31230         this.cropType = file.type;
31231         
31232         var _this = this;
31233         
31234         if(this.fireEvent('prepare', this, this.file) != false){
31235             
31236             var reader = new FileReader();
31237             
31238             reader.onload = function (e) {
31239                 if (e.target.error) {
31240                     Roo.log(e.target.error);
31241                     return;
31242                 }
31243                 
31244                 var buffer = e.target.result,
31245                     dataView = new DataView(buffer),
31246                     offset = 2,
31247                     maxOffset = dataView.byteLength - 4,
31248                     markerBytes,
31249                     markerLength;
31250                 
31251                 if (dataView.getUint16(0) === 0xffd8) {
31252                     while (offset < maxOffset) {
31253                         markerBytes = dataView.getUint16(offset);
31254                         
31255                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31256                             markerLength = dataView.getUint16(offset + 2) + 2;
31257                             if (offset + markerLength > dataView.byteLength) {
31258                                 Roo.log('Invalid meta data: Invalid segment size.');
31259                                 break;
31260                             }
31261                             
31262                             if(markerBytes == 0xffe1){
31263                                 _this.parseExifData(
31264                                     dataView,
31265                                     offset,
31266                                     markerLength
31267                                 );
31268                             }
31269                             
31270                             offset += markerLength;
31271                             
31272                             continue;
31273                         }
31274                         
31275                         break;
31276                     }
31277                     
31278                 }
31279                 
31280                 var url = _this.urlAPI.createObjectURL(_this.file);
31281                 
31282                 _this.loadCanvas(url);
31283                 
31284                 return;
31285             }
31286             
31287             reader.readAsArrayBuffer(this.file);
31288             
31289         }
31290         
31291     },
31292     
31293     parseExifData : function(dataView, offset, length)
31294     {
31295         var tiffOffset = offset + 10,
31296             littleEndian,
31297             dirOffset;
31298     
31299         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31300             // No Exif data, might be XMP data instead
31301             return;
31302         }
31303         
31304         // Check for the ASCII code for "Exif" (0x45786966):
31305         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31306             // No Exif data, might be XMP data instead
31307             return;
31308         }
31309         if (tiffOffset + 8 > dataView.byteLength) {
31310             Roo.log('Invalid Exif data: Invalid segment size.');
31311             return;
31312         }
31313         // Check for the two null bytes:
31314         if (dataView.getUint16(offset + 8) !== 0x0000) {
31315             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31316             return;
31317         }
31318         // Check the byte alignment:
31319         switch (dataView.getUint16(tiffOffset)) {
31320         case 0x4949:
31321             littleEndian = true;
31322             break;
31323         case 0x4D4D:
31324             littleEndian = false;
31325             break;
31326         default:
31327             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31328             return;
31329         }
31330         // Check for the TIFF tag marker (0x002A):
31331         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31332             Roo.log('Invalid Exif data: Missing TIFF marker.');
31333             return;
31334         }
31335         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31336         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31337         
31338         this.parseExifTags(
31339             dataView,
31340             tiffOffset,
31341             tiffOffset + dirOffset,
31342             littleEndian
31343         );
31344     },
31345     
31346     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31347     {
31348         var tagsNumber,
31349             dirEndOffset,
31350             i;
31351         if (dirOffset + 6 > dataView.byteLength) {
31352             Roo.log('Invalid Exif data: Invalid directory offset.');
31353             return;
31354         }
31355         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31356         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31357         if (dirEndOffset + 4 > dataView.byteLength) {
31358             Roo.log('Invalid Exif data: Invalid directory size.');
31359             return;
31360         }
31361         for (i = 0; i < tagsNumber; i += 1) {
31362             this.parseExifTag(
31363                 dataView,
31364                 tiffOffset,
31365                 dirOffset + 2 + 12 * i, // tag offset
31366                 littleEndian
31367             );
31368         }
31369         // Return the offset to the next directory:
31370         return dataView.getUint32(dirEndOffset, littleEndian);
31371     },
31372     
31373     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31374     {
31375         var tag = dataView.getUint16(offset, littleEndian);
31376         
31377         this.exif[tag] = this.getExifValue(
31378             dataView,
31379             tiffOffset,
31380             offset,
31381             dataView.getUint16(offset + 2, littleEndian), // tag type
31382             dataView.getUint32(offset + 4, littleEndian), // tag length
31383             littleEndian
31384         );
31385     },
31386     
31387     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31388     {
31389         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31390             tagSize,
31391             dataOffset,
31392             values,
31393             i,
31394             str,
31395             c;
31396     
31397         if (!tagType) {
31398             Roo.log('Invalid Exif data: Invalid tag type.');
31399             return;
31400         }
31401         
31402         tagSize = tagType.size * length;
31403         // Determine if the value is contained in the dataOffset bytes,
31404         // or if the value at the dataOffset is a pointer to the actual data:
31405         dataOffset = tagSize > 4 ?
31406                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31407         if (dataOffset + tagSize > dataView.byteLength) {
31408             Roo.log('Invalid Exif data: Invalid data offset.');
31409             return;
31410         }
31411         if (length === 1) {
31412             return tagType.getValue(dataView, dataOffset, littleEndian);
31413         }
31414         values = [];
31415         for (i = 0; i < length; i += 1) {
31416             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31417         }
31418         
31419         if (tagType.ascii) {
31420             str = '';
31421             // Concatenate the chars:
31422             for (i = 0; i < values.length; i += 1) {
31423                 c = values[i];
31424                 // Ignore the terminating NULL byte(s):
31425                 if (c === '\u0000') {
31426                     break;
31427                 }
31428                 str += c;
31429             }
31430             return str;
31431         }
31432         return values;
31433     }
31434     
31435 });
31436
31437 Roo.apply(Roo.bootstrap.UploadCropbox, {
31438     tags : {
31439         'Orientation': 0x0112
31440     },
31441     
31442     Orientation: {
31443             1: 0, //'top-left',
31444 //            2: 'top-right',
31445             3: 180, //'bottom-right',
31446 //            4: 'bottom-left',
31447 //            5: 'left-top',
31448             6: 90, //'right-top',
31449 //            7: 'right-bottom',
31450             8: 270 //'left-bottom'
31451     },
31452     
31453     exifTagTypes : {
31454         // byte, 8-bit unsigned int:
31455         1: {
31456             getValue: function (dataView, dataOffset) {
31457                 return dataView.getUint8(dataOffset);
31458             },
31459             size: 1
31460         },
31461         // ascii, 8-bit byte:
31462         2: {
31463             getValue: function (dataView, dataOffset) {
31464                 return String.fromCharCode(dataView.getUint8(dataOffset));
31465             },
31466             size: 1,
31467             ascii: true
31468         },
31469         // short, 16 bit int:
31470         3: {
31471             getValue: function (dataView, dataOffset, littleEndian) {
31472                 return dataView.getUint16(dataOffset, littleEndian);
31473             },
31474             size: 2
31475         },
31476         // long, 32 bit int:
31477         4: {
31478             getValue: function (dataView, dataOffset, littleEndian) {
31479                 return dataView.getUint32(dataOffset, littleEndian);
31480             },
31481             size: 4
31482         },
31483         // rational = two long values, first is numerator, second is denominator:
31484         5: {
31485             getValue: function (dataView, dataOffset, littleEndian) {
31486                 return dataView.getUint32(dataOffset, littleEndian) /
31487                     dataView.getUint32(dataOffset + 4, littleEndian);
31488             },
31489             size: 8
31490         },
31491         // slong, 32 bit signed int:
31492         9: {
31493             getValue: function (dataView, dataOffset, littleEndian) {
31494                 return dataView.getInt32(dataOffset, littleEndian);
31495             },
31496             size: 4
31497         },
31498         // srational, two slongs, first is numerator, second is denominator:
31499         10: {
31500             getValue: function (dataView, dataOffset, littleEndian) {
31501                 return dataView.getInt32(dataOffset, littleEndian) /
31502                     dataView.getInt32(dataOffset + 4, littleEndian);
31503             },
31504             size: 8
31505         }
31506     },
31507     
31508     footer : {
31509         STANDARD : [
31510             {
31511                 tag : 'div',
31512                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31513                 action : 'rotate-left',
31514                 cn : [
31515                     {
31516                         tag : 'button',
31517                         cls : 'btn btn-default',
31518                         html : '<i class="fa fa-undo"></i>'
31519                     }
31520                 ]
31521             },
31522             {
31523                 tag : 'div',
31524                 cls : 'btn-group roo-upload-cropbox-picture',
31525                 action : 'picture',
31526                 cn : [
31527                     {
31528                         tag : 'button',
31529                         cls : 'btn btn-default',
31530                         html : '<i class="fa fa-picture-o"></i>'
31531                     }
31532                 ]
31533             },
31534             {
31535                 tag : 'div',
31536                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31537                 action : 'rotate-right',
31538                 cn : [
31539                     {
31540                         tag : 'button',
31541                         cls : 'btn btn-default',
31542                         html : '<i class="fa fa-repeat"></i>'
31543                     }
31544                 ]
31545             }
31546         ],
31547         DOCUMENT : [
31548             {
31549                 tag : 'div',
31550                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31551                 action : 'rotate-left',
31552                 cn : [
31553                     {
31554                         tag : 'button',
31555                         cls : 'btn btn-default',
31556                         html : '<i class="fa fa-undo"></i>'
31557                     }
31558                 ]
31559             },
31560             {
31561                 tag : 'div',
31562                 cls : 'btn-group roo-upload-cropbox-download',
31563                 action : 'download',
31564                 cn : [
31565                     {
31566                         tag : 'button',
31567                         cls : 'btn btn-default',
31568                         html : '<i class="fa fa-download"></i>'
31569                     }
31570                 ]
31571             },
31572             {
31573                 tag : 'div',
31574                 cls : 'btn-group roo-upload-cropbox-crop',
31575                 action : 'crop',
31576                 cn : [
31577                     {
31578                         tag : 'button',
31579                         cls : 'btn btn-default',
31580                         html : '<i class="fa fa-crop"></i>'
31581                     }
31582                 ]
31583             },
31584             {
31585                 tag : 'div',
31586                 cls : 'btn-group roo-upload-cropbox-trash',
31587                 action : 'trash',
31588                 cn : [
31589                     {
31590                         tag : 'button',
31591                         cls : 'btn btn-default',
31592                         html : '<i class="fa fa-trash"></i>'
31593                     }
31594                 ]
31595             },
31596             {
31597                 tag : 'div',
31598                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31599                 action : 'rotate-right',
31600                 cn : [
31601                     {
31602                         tag : 'button',
31603                         cls : 'btn btn-default',
31604                         html : '<i class="fa fa-repeat"></i>'
31605                     }
31606                 ]
31607             }
31608         ],
31609         ROTATOR : [
31610             {
31611                 tag : 'div',
31612                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31613                 action : 'rotate-left',
31614                 cn : [
31615                     {
31616                         tag : 'button',
31617                         cls : 'btn btn-default',
31618                         html : '<i class="fa fa-undo"></i>'
31619                     }
31620                 ]
31621             },
31622             {
31623                 tag : 'div',
31624                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31625                 action : 'rotate-right',
31626                 cn : [
31627                     {
31628                         tag : 'button',
31629                         cls : 'btn btn-default',
31630                         html : '<i class="fa fa-repeat"></i>'
31631                     }
31632                 ]
31633             }
31634         ]
31635     }
31636 });
31637
31638 /*
31639 * Licence: LGPL
31640 */
31641
31642 /**
31643  * @class Roo.bootstrap.DocumentManager
31644  * @extends Roo.bootstrap.Component
31645  * Bootstrap DocumentManager class
31646  * @cfg {String} paramName default 'imageUpload'
31647  * @cfg {String} toolTipName default 'filename'
31648  * @cfg {String} method default POST
31649  * @cfg {String} url action url
31650  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31651  * @cfg {Boolean} multiple multiple upload default true
31652  * @cfg {Number} thumbSize default 300
31653  * @cfg {String} fieldLabel
31654  * @cfg {Number} labelWidth default 4
31655  * @cfg {String} labelAlign (left|top) default left
31656  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31657 * @cfg {Number} labellg set the width of label (1-12)
31658  * @cfg {Number} labelmd set the width of label (1-12)
31659  * @cfg {Number} labelsm set the width of label (1-12)
31660  * @cfg {Number} labelxs set the width of label (1-12)
31661  * 
31662  * @constructor
31663  * Create a new DocumentManager
31664  * @param {Object} config The config object
31665  */
31666
31667 Roo.bootstrap.DocumentManager = function(config){
31668     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31669     
31670     this.files = [];
31671     this.delegates = [];
31672     
31673     this.addEvents({
31674         /**
31675          * @event initial
31676          * Fire when initial the DocumentManager
31677          * @param {Roo.bootstrap.DocumentManager} this
31678          */
31679         "initial" : true,
31680         /**
31681          * @event inspect
31682          * inspect selected file
31683          * @param {Roo.bootstrap.DocumentManager} this
31684          * @param {File} file
31685          */
31686         "inspect" : true,
31687         /**
31688          * @event exception
31689          * Fire when xhr load exception
31690          * @param {Roo.bootstrap.DocumentManager} this
31691          * @param {XMLHttpRequest} xhr
31692          */
31693         "exception" : true,
31694         /**
31695          * @event afterupload
31696          * Fire when xhr load exception
31697          * @param {Roo.bootstrap.DocumentManager} this
31698          * @param {XMLHttpRequest} xhr
31699          */
31700         "afterupload" : true,
31701         /**
31702          * @event prepare
31703          * prepare the form data
31704          * @param {Roo.bootstrap.DocumentManager} this
31705          * @param {Object} formData
31706          */
31707         "prepare" : true,
31708         /**
31709          * @event remove
31710          * Fire when remove the file
31711          * @param {Roo.bootstrap.DocumentManager} this
31712          * @param {Object} file
31713          */
31714         "remove" : true,
31715         /**
31716          * @event refresh
31717          * Fire after refresh the file
31718          * @param {Roo.bootstrap.DocumentManager} this
31719          */
31720         "refresh" : true,
31721         /**
31722          * @event click
31723          * Fire after click the image
31724          * @param {Roo.bootstrap.DocumentManager} this
31725          * @param {Object} file
31726          */
31727         "click" : true,
31728         /**
31729          * @event edit
31730          * Fire when upload a image and editable set to true
31731          * @param {Roo.bootstrap.DocumentManager} this
31732          * @param {Object} file
31733          */
31734         "edit" : true,
31735         /**
31736          * @event beforeselectfile
31737          * Fire before select file
31738          * @param {Roo.bootstrap.DocumentManager} this
31739          */
31740         "beforeselectfile" : true,
31741         /**
31742          * @event process
31743          * Fire before process file
31744          * @param {Roo.bootstrap.DocumentManager} this
31745          * @param {Object} file
31746          */
31747         "process" : true,
31748         /**
31749          * @event previewrendered
31750          * Fire when preview rendered
31751          * @param {Roo.bootstrap.DocumentManager} this
31752          * @param {Object} file
31753          */
31754         "previewrendered" : true,
31755         /**
31756          */
31757         "previewResize" : true
31758         
31759     });
31760 };
31761
31762 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31763     
31764     boxes : 0,
31765     inputName : '',
31766     thumbSize : 300,
31767     multiple : true,
31768     files : false,
31769     method : 'POST',
31770     url : '',
31771     paramName : 'imageUpload',
31772     toolTipName : 'filename',
31773     fieldLabel : '',
31774     labelWidth : 4,
31775     labelAlign : 'left',
31776     editable : true,
31777     delegates : false,
31778     xhr : false, 
31779     
31780     labellg : 0,
31781     labelmd : 0,
31782     labelsm : 0,
31783     labelxs : 0,
31784     
31785     getAutoCreate : function()
31786     {   
31787         var managerWidget = {
31788             tag : 'div',
31789             cls : 'roo-document-manager',
31790             cn : [
31791                 {
31792                     tag : 'input',
31793                     cls : 'roo-document-manager-selector',
31794                     type : 'file'
31795                 },
31796                 {
31797                     tag : 'div',
31798                     cls : 'roo-document-manager-uploader',
31799                     cn : [
31800                         {
31801                             tag : 'div',
31802                             cls : 'roo-document-manager-upload-btn',
31803                             html : '<i class="fa fa-plus"></i>'
31804                         }
31805                     ]
31806                     
31807                 }
31808             ]
31809         };
31810         
31811         var content = [
31812             {
31813                 tag : 'div',
31814                 cls : 'column col-md-12',
31815                 cn : managerWidget
31816             }
31817         ];
31818         
31819         if(this.fieldLabel.length){
31820             
31821             content = [
31822                 {
31823                     tag : 'div',
31824                     cls : 'column col-md-12',
31825                     html : this.fieldLabel
31826                 },
31827                 {
31828                     tag : 'div',
31829                     cls : 'column col-md-12',
31830                     cn : managerWidget
31831                 }
31832             ];
31833
31834             if(this.labelAlign == 'left'){
31835                 content = [
31836                     {
31837                         tag : 'div',
31838                         cls : 'column',
31839                         html : this.fieldLabel
31840                     },
31841                     {
31842                         tag : 'div',
31843                         cls : 'column',
31844                         cn : managerWidget
31845                     }
31846                 ];
31847                 
31848                 if(this.labelWidth > 12){
31849                     content[0].style = "width: " + this.labelWidth + 'px';
31850                 }
31851
31852                 if(this.labelWidth < 13 && this.labelmd == 0){
31853                     this.labelmd = this.labelWidth;
31854                 }
31855
31856                 if(this.labellg > 0){
31857                     content[0].cls += ' col-lg-' + this.labellg;
31858                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31859                 }
31860
31861                 if(this.labelmd > 0){
31862                     content[0].cls += ' col-md-' + this.labelmd;
31863                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31864                 }
31865
31866                 if(this.labelsm > 0){
31867                     content[0].cls += ' col-sm-' + this.labelsm;
31868                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31869                 }
31870
31871                 if(this.labelxs > 0){
31872                     content[0].cls += ' col-xs-' + this.labelxs;
31873                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31874                 }
31875                 
31876             }
31877         }
31878         
31879         var cfg = {
31880             tag : 'div',
31881             cls : 'row clearfix',
31882             cn : content
31883         };
31884         
31885         return cfg;
31886         
31887     },
31888     
31889     initEvents : function()
31890     {
31891         this.managerEl = this.el.select('.roo-document-manager', true).first();
31892         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31893         
31894         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31895         this.selectorEl.hide();
31896         
31897         if(this.multiple){
31898             this.selectorEl.attr('multiple', 'multiple');
31899         }
31900         
31901         this.selectorEl.on('change', this.onFileSelected, this);
31902         
31903         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31904         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31905         
31906         this.uploader.on('click', this.onUploaderClick, this);
31907         
31908         this.renderProgressDialog();
31909         
31910         var _this = this;
31911         
31912         window.addEventListener("resize", function() { _this.refresh(); } );
31913         
31914         this.fireEvent('initial', this);
31915     },
31916     
31917     renderProgressDialog : function()
31918     {
31919         var _this = this;
31920         
31921         this.progressDialog = new Roo.bootstrap.Modal({
31922             cls : 'roo-document-manager-progress-dialog',
31923             allow_close : false,
31924             animate : false,
31925             title : '',
31926             buttons : [
31927                 {
31928                     name  :'cancel',
31929                     weight : 'danger',
31930                     html : 'Cancel'
31931                 }
31932             ], 
31933             listeners : { 
31934                 btnclick : function() {
31935                     _this.uploadCancel();
31936                     this.hide();
31937                 }
31938             }
31939         });
31940          
31941         this.progressDialog.render(Roo.get(document.body));
31942          
31943         this.progress = new Roo.bootstrap.Progress({
31944             cls : 'roo-document-manager-progress',
31945             active : true,
31946             striped : true
31947         });
31948         
31949         this.progress.render(this.progressDialog.getChildContainer());
31950         
31951         this.progressBar = new Roo.bootstrap.ProgressBar({
31952             cls : 'roo-document-manager-progress-bar',
31953             aria_valuenow : 0,
31954             aria_valuemin : 0,
31955             aria_valuemax : 12,
31956             panel : 'success'
31957         });
31958         
31959         this.progressBar.render(this.progress.getChildContainer());
31960     },
31961     
31962     onUploaderClick : function(e)
31963     {
31964         e.preventDefault();
31965      
31966         if(this.fireEvent('beforeselectfile', this) != false){
31967             this.selectorEl.dom.click();
31968         }
31969         
31970     },
31971     
31972     onFileSelected : function(e)
31973     {
31974         e.preventDefault();
31975         
31976         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31977             return;
31978         }
31979         
31980         Roo.each(this.selectorEl.dom.files, function(file){
31981             if(this.fireEvent('inspect', this, file) != false){
31982                 this.files.push(file);
31983             }
31984         }, this);
31985         
31986         this.queue();
31987         
31988     },
31989     
31990     queue : function()
31991     {
31992         this.selectorEl.dom.value = '';
31993         
31994         if(!this.files || !this.files.length){
31995             return;
31996         }
31997         
31998         if(this.boxes > 0 && this.files.length > this.boxes){
31999             this.files = this.files.slice(0, this.boxes);
32000         }
32001         
32002         this.uploader.show();
32003         
32004         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32005             this.uploader.hide();
32006         }
32007         
32008         var _this = this;
32009         
32010         var files = [];
32011         
32012         var docs = [];
32013         
32014         Roo.each(this.files, function(file){
32015             
32016             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32017                 var f = this.renderPreview(file);
32018                 files.push(f);
32019                 return;
32020             }
32021             
32022             if(file.type.indexOf('image') != -1){
32023                 this.delegates.push(
32024                     (function(){
32025                         _this.process(file);
32026                     }).createDelegate(this)
32027                 );
32028         
32029                 return;
32030             }
32031             
32032             docs.push(
32033                 (function(){
32034                     _this.process(file);
32035                 }).createDelegate(this)
32036             );
32037             
32038         }, this);
32039         
32040         this.files = files;
32041         
32042         this.delegates = this.delegates.concat(docs);
32043         
32044         if(!this.delegates.length){
32045             this.refresh();
32046             return;
32047         }
32048         
32049         this.progressBar.aria_valuemax = this.delegates.length;
32050         
32051         this.arrange();
32052         
32053         return;
32054     },
32055     
32056     arrange : function()
32057     {
32058         if(!this.delegates.length){
32059             this.progressDialog.hide();
32060             this.refresh();
32061             return;
32062         }
32063         
32064         var delegate = this.delegates.shift();
32065         
32066         this.progressDialog.show();
32067         
32068         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32069         
32070         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32071         
32072         delegate();
32073     },
32074     
32075     refresh : function()
32076     {
32077         this.uploader.show();
32078         
32079         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32080             this.uploader.hide();
32081         }
32082         
32083         Roo.isTouch ? this.closable(false) : this.closable(true);
32084         
32085         this.fireEvent('refresh', this);
32086     },
32087     
32088     onRemove : function(e, el, o)
32089     {
32090         e.preventDefault();
32091         
32092         this.fireEvent('remove', this, o);
32093         
32094     },
32095     
32096     remove : function(o)
32097     {
32098         var files = [];
32099         
32100         Roo.each(this.files, function(file){
32101             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32102                 files.push(file);
32103                 return;
32104             }
32105
32106             o.target.remove();
32107
32108         }, this);
32109         
32110         this.files = files;
32111         
32112         this.refresh();
32113     },
32114     
32115     clear : function()
32116     {
32117         Roo.each(this.files, function(file){
32118             if(!file.target){
32119                 return;
32120             }
32121             
32122             file.target.remove();
32123
32124         }, this);
32125         
32126         this.files = [];
32127         
32128         this.refresh();
32129     },
32130     
32131     onClick : function(e, el, o)
32132     {
32133         e.preventDefault();
32134         
32135         this.fireEvent('click', this, o);
32136         
32137     },
32138     
32139     closable : function(closable)
32140     {
32141         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32142             
32143             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32144             
32145             if(closable){
32146                 el.show();
32147                 return;
32148             }
32149             
32150             el.hide();
32151             
32152         }, this);
32153     },
32154     
32155     xhrOnLoad : function(xhr)
32156     {
32157         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32158             el.remove();
32159         }, this);
32160         
32161         if (xhr.readyState !== 4) {
32162             this.arrange();
32163             this.fireEvent('exception', this, xhr);
32164             return;
32165         }
32166
32167         var response = Roo.decode(xhr.responseText);
32168         
32169         if(!response.success){
32170             this.arrange();
32171             this.fireEvent('exception', this, xhr);
32172             return;
32173         }
32174         
32175         var file = this.renderPreview(response.data);
32176         
32177         this.files.push(file);
32178         
32179         this.arrange();
32180         
32181         this.fireEvent('afterupload', this, xhr);
32182         
32183     },
32184     
32185     xhrOnError : function(xhr)
32186     {
32187         Roo.log('xhr on error');
32188         
32189         var response = Roo.decode(xhr.responseText);
32190           
32191         Roo.log(response);
32192         
32193         this.arrange();
32194     },
32195     
32196     process : function(file)
32197     {
32198         if(this.fireEvent('process', this, file) !== false){
32199             if(this.editable && file.type.indexOf('image') != -1){
32200                 this.fireEvent('edit', this, file);
32201                 return;
32202             }
32203
32204             this.uploadStart(file, false);
32205
32206             return;
32207         }
32208         
32209     },
32210     
32211     uploadStart : function(file, crop)
32212     {
32213         this.xhr = new XMLHttpRequest();
32214         
32215         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32216             this.arrange();
32217             return;
32218         }
32219         
32220         file.xhr = this.xhr;
32221             
32222         this.managerEl.createChild({
32223             tag : 'div',
32224             cls : 'roo-document-manager-loading',
32225             cn : [
32226                 {
32227                     tag : 'div',
32228                     tooltip : file.name,
32229                     cls : 'roo-document-manager-thumb',
32230                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32231                 }
32232             ]
32233
32234         });
32235
32236         this.xhr.open(this.method, this.url, true);
32237         
32238         var headers = {
32239             "Accept": "application/json",
32240             "Cache-Control": "no-cache",
32241             "X-Requested-With": "XMLHttpRequest"
32242         };
32243         
32244         for (var headerName in headers) {
32245             var headerValue = headers[headerName];
32246             if (headerValue) {
32247                 this.xhr.setRequestHeader(headerName, headerValue);
32248             }
32249         }
32250         
32251         var _this = this;
32252         
32253         this.xhr.onload = function()
32254         {
32255             _this.xhrOnLoad(_this.xhr);
32256         }
32257         
32258         this.xhr.onerror = function()
32259         {
32260             _this.xhrOnError(_this.xhr);
32261         }
32262         
32263         var formData = new FormData();
32264
32265         formData.append('returnHTML', 'NO');
32266         
32267         if(crop){
32268             formData.append('crop', crop);
32269         }
32270         
32271         formData.append(this.paramName, file, file.name);
32272         
32273         var options = {
32274             file : file, 
32275             manually : false
32276         };
32277         
32278         if(this.fireEvent('prepare', this, formData, options) != false){
32279             
32280             if(options.manually){
32281                 return;
32282             }
32283             
32284             this.xhr.send(formData);
32285             return;
32286         };
32287         
32288         this.uploadCancel();
32289     },
32290     
32291     uploadCancel : function()
32292     {
32293         if (this.xhr) {
32294             this.xhr.abort();
32295         }
32296         
32297         this.delegates = [];
32298         
32299         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32300             el.remove();
32301         }, this);
32302         
32303         this.arrange();
32304     },
32305     
32306     renderPreview : function(file)
32307     {
32308         if(typeof(file.target) != 'undefined' && file.target){
32309             return file;
32310         }
32311         
32312         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32313         
32314         var previewEl = this.managerEl.createChild({
32315             tag : 'div',
32316             cls : 'roo-document-manager-preview',
32317             cn : [
32318                 {
32319                     tag : 'div',
32320                     tooltip : file[this.toolTipName],
32321                     cls : 'roo-document-manager-thumb',
32322                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32323                 },
32324                 {
32325                     tag : 'button',
32326                     cls : 'close',
32327                     html : '<i class="fa fa-times-circle"></i>'
32328                 }
32329             ]
32330         });
32331
32332         var close = previewEl.select('button.close', true).first();
32333
32334         close.on('click', this.onRemove, this, file);
32335
32336         file.target = previewEl;
32337
32338         var image = previewEl.select('img', true).first();
32339         
32340         var _this = this;
32341         
32342         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32343         
32344         image.on('click', this.onClick, this, file);
32345         
32346         this.fireEvent('previewrendered', this, file);
32347         
32348         return file;
32349         
32350     },
32351     
32352     onPreviewLoad : function(file, image)
32353     {
32354         if(typeof(file.target) == 'undefined' || !file.target){
32355             return;
32356         }
32357         
32358         var width = image.dom.naturalWidth || image.dom.width;
32359         var height = image.dom.naturalHeight || image.dom.height;
32360         
32361         if(!this.previewResize) {
32362             return;
32363         }
32364         
32365         if(width > height){
32366             file.target.addClass('wide');
32367             return;
32368         }
32369         
32370         file.target.addClass('tall');
32371         return;
32372         
32373     },
32374     
32375     uploadFromSource : function(file, crop)
32376     {
32377         this.xhr = new XMLHttpRequest();
32378         
32379         this.managerEl.createChild({
32380             tag : 'div',
32381             cls : 'roo-document-manager-loading',
32382             cn : [
32383                 {
32384                     tag : 'div',
32385                     tooltip : file.name,
32386                     cls : 'roo-document-manager-thumb',
32387                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32388                 }
32389             ]
32390
32391         });
32392
32393         this.xhr.open(this.method, this.url, true);
32394         
32395         var headers = {
32396             "Accept": "application/json",
32397             "Cache-Control": "no-cache",
32398             "X-Requested-With": "XMLHttpRequest"
32399         };
32400         
32401         for (var headerName in headers) {
32402             var headerValue = headers[headerName];
32403             if (headerValue) {
32404                 this.xhr.setRequestHeader(headerName, headerValue);
32405             }
32406         }
32407         
32408         var _this = this;
32409         
32410         this.xhr.onload = function()
32411         {
32412             _this.xhrOnLoad(_this.xhr);
32413         }
32414         
32415         this.xhr.onerror = function()
32416         {
32417             _this.xhrOnError(_this.xhr);
32418         }
32419         
32420         var formData = new FormData();
32421
32422         formData.append('returnHTML', 'NO');
32423         
32424         formData.append('crop', crop);
32425         
32426         if(typeof(file.filename) != 'undefined'){
32427             formData.append('filename', file.filename);
32428         }
32429         
32430         if(typeof(file.mimetype) != 'undefined'){
32431             formData.append('mimetype', file.mimetype);
32432         }
32433         
32434         Roo.log(formData);
32435         
32436         if(this.fireEvent('prepare', this, formData) != false){
32437             this.xhr.send(formData);
32438         };
32439     }
32440 });
32441
32442 /*
32443 * Licence: LGPL
32444 */
32445
32446 /**
32447  * @class Roo.bootstrap.DocumentViewer
32448  * @extends Roo.bootstrap.Component
32449  * Bootstrap DocumentViewer class
32450  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32451  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32452  * 
32453  * @constructor
32454  * Create a new DocumentViewer
32455  * @param {Object} config The config object
32456  */
32457
32458 Roo.bootstrap.DocumentViewer = function(config){
32459     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32460     
32461     this.addEvents({
32462         /**
32463          * @event initial
32464          * Fire after initEvent
32465          * @param {Roo.bootstrap.DocumentViewer} this
32466          */
32467         "initial" : true,
32468         /**
32469          * @event click
32470          * Fire after click
32471          * @param {Roo.bootstrap.DocumentViewer} this
32472          */
32473         "click" : true,
32474         /**
32475          * @event download
32476          * Fire after download button
32477          * @param {Roo.bootstrap.DocumentViewer} this
32478          */
32479         "download" : true,
32480         /**
32481          * @event trash
32482          * Fire after trash button
32483          * @param {Roo.bootstrap.DocumentViewer} this
32484          */
32485         "trash" : true
32486         
32487     });
32488 };
32489
32490 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32491     
32492     showDownload : true,
32493     
32494     showTrash : true,
32495     
32496     getAutoCreate : function()
32497     {
32498         var cfg = {
32499             tag : 'div',
32500             cls : 'roo-document-viewer',
32501             cn : [
32502                 {
32503                     tag : 'div',
32504                     cls : 'roo-document-viewer-body',
32505                     cn : [
32506                         {
32507                             tag : 'div',
32508                             cls : 'roo-document-viewer-thumb',
32509                             cn : [
32510                                 {
32511                                     tag : 'img',
32512                                     cls : 'roo-document-viewer-image'
32513                                 }
32514                             ]
32515                         }
32516                     ]
32517                 },
32518                 {
32519                     tag : 'div',
32520                     cls : 'roo-document-viewer-footer',
32521                     cn : {
32522                         tag : 'div',
32523                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32524                         cn : [
32525                             {
32526                                 tag : 'div',
32527                                 cls : 'btn-group roo-document-viewer-download',
32528                                 cn : [
32529                                     {
32530                                         tag : 'button',
32531                                         cls : 'btn btn-default',
32532                                         html : '<i class="fa fa-download"></i>'
32533                                     }
32534                                 ]
32535                             },
32536                             {
32537                                 tag : 'div',
32538                                 cls : 'btn-group roo-document-viewer-trash',
32539                                 cn : [
32540                                     {
32541                                         tag : 'button',
32542                                         cls : 'btn btn-default',
32543                                         html : '<i class="fa fa-trash"></i>'
32544                                     }
32545                                 ]
32546                             }
32547                         ]
32548                     }
32549                 }
32550             ]
32551         };
32552         
32553         return cfg;
32554     },
32555     
32556     initEvents : function()
32557     {
32558         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32559         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32560         
32561         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32562         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32563         
32564         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32565         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32566         
32567         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32568         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32569         
32570         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32571         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32572         
32573         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32574         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32575         
32576         this.bodyEl.on('click', this.onClick, this);
32577         this.downloadBtn.on('click', this.onDownload, this);
32578         this.trashBtn.on('click', this.onTrash, this);
32579         
32580         this.downloadBtn.hide();
32581         this.trashBtn.hide();
32582         
32583         if(this.showDownload){
32584             this.downloadBtn.show();
32585         }
32586         
32587         if(this.showTrash){
32588             this.trashBtn.show();
32589         }
32590         
32591         if(!this.showDownload && !this.showTrash) {
32592             this.footerEl.hide();
32593         }
32594         
32595     },
32596     
32597     initial : function()
32598     {
32599         this.fireEvent('initial', this);
32600         
32601     },
32602     
32603     onClick : function(e)
32604     {
32605         e.preventDefault();
32606         
32607         this.fireEvent('click', this);
32608     },
32609     
32610     onDownload : function(e)
32611     {
32612         e.preventDefault();
32613         
32614         this.fireEvent('download', this);
32615     },
32616     
32617     onTrash : function(e)
32618     {
32619         e.preventDefault();
32620         
32621         this.fireEvent('trash', this);
32622     }
32623     
32624 });
32625 /*
32626  * - LGPL
32627  *
32628  * nav progress bar
32629  * 
32630  */
32631
32632 /**
32633  * @class Roo.bootstrap.NavProgressBar
32634  * @extends Roo.bootstrap.Component
32635  * Bootstrap NavProgressBar class
32636  * 
32637  * @constructor
32638  * Create a new nav progress bar
32639  * @param {Object} config The config object
32640  */
32641
32642 Roo.bootstrap.NavProgressBar = function(config){
32643     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32644
32645     this.bullets = this.bullets || [];
32646    
32647 //    Roo.bootstrap.NavProgressBar.register(this);
32648      this.addEvents({
32649         /**
32650              * @event changed
32651              * Fires when the active item changes
32652              * @param {Roo.bootstrap.NavProgressBar} this
32653              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32654              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32655          */
32656         'changed': true
32657      });
32658     
32659 };
32660
32661 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32662     
32663     bullets : [],
32664     barItems : [],
32665     
32666     getAutoCreate : function()
32667     {
32668         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32669         
32670         cfg = {
32671             tag : 'div',
32672             cls : 'roo-navigation-bar-group',
32673             cn : [
32674                 {
32675                     tag : 'div',
32676                     cls : 'roo-navigation-top-bar'
32677                 },
32678                 {
32679                     tag : 'div',
32680                     cls : 'roo-navigation-bullets-bar',
32681                     cn : [
32682                         {
32683                             tag : 'ul',
32684                             cls : 'roo-navigation-bar'
32685                         }
32686                     ]
32687                 },
32688                 
32689                 {
32690                     tag : 'div',
32691                     cls : 'roo-navigation-bottom-bar'
32692                 }
32693             ]
32694             
32695         };
32696         
32697         return cfg;
32698         
32699     },
32700     
32701     initEvents: function() 
32702     {
32703         
32704     },
32705     
32706     onRender : function(ct, position) 
32707     {
32708         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32709         
32710         if(this.bullets.length){
32711             Roo.each(this.bullets, function(b){
32712                this.addItem(b);
32713             }, this);
32714         }
32715         
32716         this.format();
32717         
32718     },
32719     
32720     addItem : function(cfg)
32721     {
32722         var item = new Roo.bootstrap.NavProgressItem(cfg);
32723         
32724         item.parentId = this.id;
32725         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32726         
32727         if(cfg.html){
32728             var top = new Roo.bootstrap.Element({
32729                 tag : 'div',
32730                 cls : 'roo-navigation-bar-text'
32731             });
32732             
32733             var bottom = new Roo.bootstrap.Element({
32734                 tag : 'div',
32735                 cls : 'roo-navigation-bar-text'
32736             });
32737             
32738             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32739             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32740             
32741             var topText = new Roo.bootstrap.Element({
32742                 tag : 'span',
32743                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32744             });
32745             
32746             var bottomText = new Roo.bootstrap.Element({
32747                 tag : 'span',
32748                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32749             });
32750             
32751             topText.onRender(top.el, null);
32752             bottomText.onRender(bottom.el, null);
32753             
32754             item.topEl = top;
32755             item.bottomEl = bottom;
32756         }
32757         
32758         this.barItems.push(item);
32759         
32760         return item;
32761     },
32762     
32763     getActive : function()
32764     {
32765         var active = false;
32766         
32767         Roo.each(this.barItems, function(v){
32768             
32769             if (!v.isActive()) {
32770                 return;
32771             }
32772             
32773             active = v;
32774             return false;
32775             
32776         });
32777         
32778         return active;
32779     },
32780     
32781     setActiveItem : function(item)
32782     {
32783         var prev = false;
32784         
32785         Roo.each(this.barItems, function(v){
32786             if (v.rid == item.rid) {
32787                 return ;
32788             }
32789             
32790             if (v.isActive()) {
32791                 v.setActive(false);
32792                 prev = v;
32793             }
32794         });
32795
32796         item.setActive(true);
32797         
32798         this.fireEvent('changed', this, item, prev);
32799     },
32800     
32801     getBarItem: function(rid)
32802     {
32803         var ret = false;
32804         
32805         Roo.each(this.barItems, function(e) {
32806             if (e.rid != rid) {
32807                 return;
32808             }
32809             
32810             ret =  e;
32811             return false;
32812         });
32813         
32814         return ret;
32815     },
32816     
32817     indexOfItem : function(item)
32818     {
32819         var index = false;
32820         
32821         Roo.each(this.barItems, function(v, i){
32822             
32823             if (v.rid != item.rid) {
32824                 return;
32825             }
32826             
32827             index = i;
32828             return false
32829         });
32830         
32831         return index;
32832     },
32833     
32834     setActiveNext : function()
32835     {
32836         var i = this.indexOfItem(this.getActive());
32837         
32838         if (i > this.barItems.length) {
32839             return;
32840         }
32841         
32842         this.setActiveItem(this.barItems[i+1]);
32843     },
32844     
32845     setActivePrev : function()
32846     {
32847         var i = this.indexOfItem(this.getActive());
32848         
32849         if (i  < 1) {
32850             return;
32851         }
32852         
32853         this.setActiveItem(this.barItems[i-1]);
32854     },
32855     
32856     format : function()
32857     {
32858         if(!this.barItems.length){
32859             return;
32860         }
32861      
32862         var width = 100 / this.barItems.length;
32863         
32864         Roo.each(this.barItems, function(i){
32865             i.el.setStyle('width', width + '%');
32866             i.topEl.el.setStyle('width', width + '%');
32867             i.bottomEl.el.setStyle('width', width + '%');
32868         }, this);
32869         
32870     }
32871     
32872 });
32873 /*
32874  * - LGPL
32875  *
32876  * Nav Progress Item
32877  * 
32878  */
32879
32880 /**
32881  * @class Roo.bootstrap.NavProgressItem
32882  * @extends Roo.bootstrap.Component
32883  * Bootstrap NavProgressItem class
32884  * @cfg {String} rid the reference id
32885  * @cfg {Boolean} active (true|false) Is item active default false
32886  * @cfg {Boolean} disabled (true|false) Is item active default false
32887  * @cfg {String} html
32888  * @cfg {String} position (top|bottom) text position default bottom
32889  * @cfg {String} icon show icon instead of number
32890  * 
32891  * @constructor
32892  * Create a new NavProgressItem
32893  * @param {Object} config The config object
32894  */
32895 Roo.bootstrap.NavProgressItem = function(config){
32896     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32897     this.addEvents({
32898         // raw events
32899         /**
32900          * @event click
32901          * The raw click event for the entire grid.
32902          * @param {Roo.bootstrap.NavProgressItem} this
32903          * @param {Roo.EventObject} e
32904          */
32905         "click" : true
32906     });
32907    
32908 };
32909
32910 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32911     
32912     rid : '',
32913     active : false,
32914     disabled : false,
32915     html : '',
32916     position : 'bottom',
32917     icon : false,
32918     
32919     getAutoCreate : function()
32920     {
32921         var iconCls = 'roo-navigation-bar-item-icon';
32922         
32923         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32924         
32925         var cfg = {
32926             tag: 'li',
32927             cls: 'roo-navigation-bar-item',
32928             cn : [
32929                 {
32930                     tag : 'i',
32931                     cls : iconCls
32932                 }
32933             ]
32934         };
32935         
32936         if(this.active){
32937             cfg.cls += ' active';
32938         }
32939         if(this.disabled){
32940             cfg.cls += ' disabled';
32941         }
32942         
32943         return cfg;
32944     },
32945     
32946     disable : function()
32947     {
32948         this.setDisabled(true);
32949     },
32950     
32951     enable : function()
32952     {
32953         this.setDisabled(false);
32954     },
32955     
32956     initEvents: function() 
32957     {
32958         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32959         
32960         this.iconEl.on('click', this.onClick, this);
32961     },
32962     
32963     onClick : function(e)
32964     {
32965         e.preventDefault();
32966         
32967         if(this.disabled){
32968             return;
32969         }
32970         
32971         if(this.fireEvent('click', this, e) === false){
32972             return;
32973         };
32974         
32975         this.parent().setActiveItem(this);
32976     },
32977     
32978     isActive: function () 
32979     {
32980         return this.active;
32981     },
32982     
32983     setActive : function(state)
32984     {
32985         if(this.active == state){
32986             return;
32987         }
32988         
32989         this.active = state;
32990         
32991         if (state) {
32992             this.el.addClass('active');
32993             return;
32994         }
32995         
32996         this.el.removeClass('active');
32997         
32998         return;
32999     },
33000     
33001     setDisabled : function(state)
33002     {
33003         if(this.disabled == state){
33004             return;
33005         }
33006         
33007         this.disabled = state;
33008         
33009         if (state) {
33010             this.el.addClass('disabled');
33011             return;
33012         }
33013         
33014         this.el.removeClass('disabled');
33015     },
33016     
33017     tooltipEl : function()
33018     {
33019         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33020     }
33021 });
33022  
33023
33024  /*
33025  * - LGPL
33026  *
33027  * FieldLabel
33028  * 
33029  */
33030
33031 /**
33032  * @class Roo.bootstrap.FieldLabel
33033  * @extends Roo.bootstrap.Component
33034  * Bootstrap FieldLabel class
33035  * @cfg {String} html contents of the element
33036  * @cfg {String} tag tag of the element default label
33037  * @cfg {String} cls class of the element
33038  * @cfg {String} target label target 
33039  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33040  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33041  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33042  * @cfg {String} iconTooltip default "This field is required"
33043  * @cfg {String} indicatorpos (left|right) default left
33044  * 
33045  * @constructor
33046  * Create a new FieldLabel
33047  * @param {Object} config The config object
33048  */
33049
33050 Roo.bootstrap.FieldLabel = function(config){
33051     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33052     
33053     this.addEvents({
33054             /**
33055              * @event invalid
33056              * Fires after the field has been marked as invalid.
33057              * @param {Roo.form.FieldLabel} this
33058              * @param {String} msg The validation message
33059              */
33060             invalid : true,
33061             /**
33062              * @event valid
33063              * Fires after the field has been validated with no errors.
33064              * @param {Roo.form.FieldLabel} this
33065              */
33066             valid : true
33067         });
33068 };
33069
33070 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33071     
33072     tag: 'label',
33073     cls: '',
33074     html: '',
33075     target: '',
33076     allowBlank : true,
33077     invalidClass : 'has-warning',
33078     validClass : 'has-success',
33079     iconTooltip : 'This field is required',
33080     indicatorpos : 'left',
33081     
33082     getAutoCreate : function(){
33083         
33084         var cls = "";
33085         if (!this.allowBlank) {
33086             cls  = "visible";
33087         }
33088         
33089         var cfg = {
33090             tag : this.tag,
33091             cls : 'roo-bootstrap-field-label ' + this.cls,
33092             for : this.target,
33093             cn : [
33094                 {
33095                     tag : 'i',
33096                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33097                     tooltip : this.iconTooltip
33098                 },
33099                 {
33100                     tag : 'span',
33101                     html : this.html
33102                 }
33103             ] 
33104         };
33105         
33106         if(this.indicatorpos == 'right'){
33107             var cfg = {
33108                 tag : this.tag,
33109                 cls : 'roo-bootstrap-field-label ' + this.cls,
33110                 for : this.target,
33111                 cn : [
33112                     {
33113                         tag : 'span',
33114                         html : this.html
33115                     },
33116                     {
33117                         tag : 'i',
33118                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33119                         tooltip : this.iconTooltip
33120                     }
33121                 ] 
33122             };
33123         }
33124         
33125         return cfg;
33126     },
33127     
33128     initEvents: function() 
33129     {
33130         Roo.bootstrap.Element.superclass.initEvents.call(this);
33131         
33132         this.indicator = this.indicatorEl();
33133         
33134         if(this.indicator){
33135             this.indicator.removeClass('visible');
33136             this.indicator.addClass('invisible');
33137         }
33138         
33139         Roo.bootstrap.FieldLabel.register(this);
33140     },
33141     
33142     indicatorEl : function()
33143     {
33144         var indicator = this.el.select('i.roo-required-indicator',true).first();
33145         
33146         if(!indicator){
33147             return false;
33148         }
33149         
33150         return indicator;
33151         
33152     },
33153     
33154     /**
33155      * Mark this field as valid
33156      */
33157     markValid : function()
33158     {
33159         if(this.indicator){
33160             this.indicator.removeClass('visible');
33161             this.indicator.addClass('invisible');
33162         }
33163         if (Roo.bootstrap.version == 3) {
33164             this.el.removeClass(this.invalidClass);
33165             this.el.addClass(this.validClass);
33166         } else {
33167             this.el.removeClass('is-invalid');
33168             this.el.addClass('is-valid');
33169         }
33170         
33171         
33172         this.fireEvent('valid', this);
33173     },
33174     
33175     /**
33176      * Mark this field as invalid
33177      * @param {String} msg The validation message
33178      */
33179     markInvalid : function(msg)
33180     {
33181         if(this.indicator){
33182             this.indicator.removeClass('invisible');
33183             this.indicator.addClass('visible');
33184         }
33185           if (Roo.bootstrap.version == 3) {
33186             this.el.removeClass(this.validClass);
33187             this.el.addClass(this.invalidClass);
33188         } else {
33189             this.el.removeClass('is-valid');
33190             this.el.addClass('is-invalid');
33191         }
33192         
33193         
33194         this.fireEvent('invalid', this, msg);
33195     }
33196     
33197    
33198 });
33199
33200 Roo.apply(Roo.bootstrap.FieldLabel, {
33201     
33202     groups: {},
33203     
33204      /**
33205     * register a FieldLabel Group
33206     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33207     */
33208     register : function(label)
33209     {
33210         if(this.groups.hasOwnProperty(label.target)){
33211             return;
33212         }
33213      
33214         this.groups[label.target] = label;
33215         
33216     },
33217     /**
33218     * fetch a FieldLabel Group based on the target
33219     * @param {string} target
33220     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33221     */
33222     get: function(target) {
33223         if (typeof(this.groups[target]) == 'undefined') {
33224             return false;
33225         }
33226         
33227         return this.groups[target] ;
33228     }
33229 });
33230
33231  
33232
33233  /*
33234  * - LGPL
33235  *
33236  * page DateSplitField.
33237  * 
33238  */
33239
33240
33241 /**
33242  * @class Roo.bootstrap.DateSplitField
33243  * @extends Roo.bootstrap.Component
33244  * Bootstrap DateSplitField class
33245  * @cfg {string} fieldLabel - the label associated
33246  * @cfg {Number} labelWidth set the width of label (0-12)
33247  * @cfg {String} labelAlign (top|left)
33248  * @cfg {Boolean} dayAllowBlank (true|false) default false
33249  * @cfg {Boolean} monthAllowBlank (true|false) default false
33250  * @cfg {Boolean} yearAllowBlank (true|false) default false
33251  * @cfg {string} dayPlaceholder 
33252  * @cfg {string} monthPlaceholder
33253  * @cfg {string} yearPlaceholder
33254  * @cfg {string} dayFormat default 'd'
33255  * @cfg {string} monthFormat default 'm'
33256  * @cfg {string} yearFormat default 'Y'
33257  * @cfg {Number} labellg set the width of label (1-12)
33258  * @cfg {Number} labelmd set the width of label (1-12)
33259  * @cfg {Number} labelsm set the width of label (1-12)
33260  * @cfg {Number} labelxs set the width of label (1-12)
33261
33262  *     
33263  * @constructor
33264  * Create a new DateSplitField
33265  * @param {Object} config The config object
33266  */
33267
33268 Roo.bootstrap.DateSplitField = function(config){
33269     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33270     
33271     this.addEvents({
33272         // raw events
33273          /**
33274          * @event years
33275          * getting the data of years
33276          * @param {Roo.bootstrap.DateSplitField} this
33277          * @param {Object} years
33278          */
33279         "years" : true,
33280         /**
33281          * @event days
33282          * getting the data of days
33283          * @param {Roo.bootstrap.DateSplitField} this
33284          * @param {Object} days
33285          */
33286         "days" : true,
33287         /**
33288          * @event invalid
33289          * Fires after the field has been marked as invalid.
33290          * @param {Roo.form.Field} this
33291          * @param {String} msg The validation message
33292          */
33293         invalid : true,
33294        /**
33295          * @event valid
33296          * Fires after the field has been validated with no errors.
33297          * @param {Roo.form.Field} this
33298          */
33299         valid : true
33300     });
33301 };
33302
33303 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33304     
33305     fieldLabel : '',
33306     labelAlign : 'top',
33307     labelWidth : 3,
33308     dayAllowBlank : false,
33309     monthAllowBlank : false,
33310     yearAllowBlank : false,
33311     dayPlaceholder : '',
33312     monthPlaceholder : '',
33313     yearPlaceholder : '',
33314     dayFormat : 'd',
33315     monthFormat : 'm',
33316     yearFormat : 'Y',
33317     isFormField : true,
33318     labellg : 0,
33319     labelmd : 0,
33320     labelsm : 0,
33321     labelxs : 0,
33322     
33323     getAutoCreate : function()
33324     {
33325         var cfg = {
33326             tag : 'div',
33327             cls : 'row roo-date-split-field-group',
33328             cn : [
33329                 {
33330                     tag : 'input',
33331                     type : 'hidden',
33332                     cls : 'form-hidden-field roo-date-split-field-group-value',
33333                     name : this.name
33334                 }
33335             ]
33336         };
33337         
33338         var labelCls = 'col-md-12';
33339         var contentCls = 'col-md-4';
33340         
33341         if(this.fieldLabel){
33342             
33343             var label = {
33344                 tag : 'div',
33345                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33346                 cn : [
33347                     {
33348                         tag : 'label',
33349                         html : this.fieldLabel
33350                     }
33351                 ]
33352             };
33353             
33354             if(this.labelAlign == 'left'){
33355             
33356                 if(this.labelWidth > 12){
33357                     label.style = "width: " + this.labelWidth + 'px';
33358                 }
33359
33360                 if(this.labelWidth < 13 && this.labelmd == 0){
33361                     this.labelmd = this.labelWidth;
33362                 }
33363
33364                 if(this.labellg > 0){
33365                     labelCls = ' col-lg-' + this.labellg;
33366                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33367                 }
33368
33369                 if(this.labelmd > 0){
33370                     labelCls = ' col-md-' + this.labelmd;
33371                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33372                 }
33373
33374                 if(this.labelsm > 0){
33375                     labelCls = ' col-sm-' + this.labelsm;
33376                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33377                 }
33378
33379                 if(this.labelxs > 0){
33380                     labelCls = ' col-xs-' + this.labelxs;
33381                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33382                 }
33383             }
33384             
33385             label.cls += ' ' + labelCls;
33386             
33387             cfg.cn.push(label);
33388         }
33389         
33390         Roo.each(['day', 'month', 'year'], function(t){
33391             cfg.cn.push({
33392                 tag : 'div',
33393                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33394             });
33395         }, this);
33396         
33397         return cfg;
33398     },
33399     
33400     inputEl: function ()
33401     {
33402         return this.el.select('.roo-date-split-field-group-value', true).first();
33403     },
33404     
33405     onRender : function(ct, position) 
33406     {
33407         var _this = this;
33408         
33409         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33410         
33411         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33412         
33413         this.dayField = new Roo.bootstrap.ComboBox({
33414             allowBlank : this.dayAllowBlank,
33415             alwaysQuery : true,
33416             displayField : 'value',
33417             editable : false,
33418             fieldLabel : '',
33419             forceSelection : true,
33420             mode : 'local',
33421             placeholder : this.dayPlaceholder,
33422             selectOnFocus : true,
33423             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33424             triggerAction : 'all',
33425             typeAhead : true,
33426             valueField : 'value',
33427             store : new Roo.data.SimpleStore({
33428                 data : (function() {    
33429                     var days = [];
33430                     _this.fireEvent('days', _this, days);
33431                     return days;
33432                 })(),
33433                 fields : [ 'value' ]
33434             }),
33435             listeners : {
33436                 select : function (_self, record, index)
33437                 {
33438                     _this.setValue(_this.getValue());
33439                 }
33440             }
33441         });
33442
33443         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33444         
33445         this.monthField = new Roo.bootstrap.MonthField({
33446             after : '<i class=\"fa fa-calendar\"></i>',
33447             allowBlank : this.monthAllowBlank,
33448             placeholder : this.monthPlaceholder,
33449             readOnly : true,
33450             listeners : {
33451                 render : function (_self)
33452                 {
33453                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33454                         e.preventDefault();
33455                         _self.focus();
33456                     });
33457                 },
33458                 select : function (_self, oldvalue, newvalue)
33459                 {
33460                     _this.setValue(_this.getValue());
33461                 }
33462             }
33463         });
33464         
33465         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33466         
33467         this.yearField = new Roo.bootstrap.ComboBox({
33468             allowBlank : this.yearAllowBlank,
33469             alwaysQuery : true,
33470             displayField : 'value',
33471             editable : false,
33472             fieldLabel : '',
33473             forceSelection : true,
33474             mode : 'local',
33475             placeholder : this.yearPlaceholder,
33476             selectOnFocus : true,
33477             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33478             triggerAction : 'all',
33479             typeAhead : true,
33480             valueField : 'value',
33481             store : new Roo.data.SimpleStore({
33482                 data : (function() {
33483                     var years = [];
33484                     _this.fireEvent('years', _this, years);
33485                     return years;
33486                 })(),
33487                 fields : [ 'value' ]
33488             }),
33489             listeners : {
33490                 select : function (_self, record, index)
33491                 {
33492                     _this.setValue(_this.getValue());
33493                 }
33494             }
33495         });
33496
33497         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33498     },
33499     
33500     setValue : function(v, format)
33501     {
33502         this.inputEl.dom.value = v;
33503         
33504         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33505         
33506         var d = Date.parseDate(v, f);
33507         
33508         if(!d){
33509             this.validate();
33510             return;
33511         }
33512         
33513         this.setDay(d.format(this.dayFormat));
33514         this.setMonth(d.format(this.monthFormat));
33515         this.setYear(d.format(this.yearFormat));
33516         
33517         this.validate();
33518         
33519         return;
33520     },
33521     
33522     setDay : function(v)
33523     {
33524         this.dayField.setValue(v);
33525         this.inputEl.dom.value = this.getValue();
33526         this.validate();
33527         return;
33528     },
33529     
33530     setMonth : function(v)
33531     {
33532         this.monthField.setValue(v, true);
33533         this.inputEl.dom.value = this.getValue();
33534         this.validate();
33535         return;
33536     },
33537     
33538     setYear : function(v)
33539     {
33540         this.yearField.setValue(v);
33541         this.inputEl.dom.value = this.getValue();
33542         this.validate();
33543         return;
33544     },
33545     
33546     getDay : function()
33547     {
33548         return this.dayField.getValue();
33549     },
33550     
33551     getMonth : function()
33552     {
33553         return this.monthField.getValue();
33554     },
33555     
33556     getYear : function()
33557     {
33558         return this.yearField.getValue();
33559     },
33560     
33561     getValue : function()
33562     {
33563         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33564         
33565         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33566         
33567         return date;
33568     },
33569     
33570     reset : function()
33571     {
33572         this.setDay('');
33573         this.setMonth('');
33574         this.setYear('');
33575         this.inputEl.dom.value = '';
33576         this.validate();
33577         return;
33578     },
33579     
33580     validate : function()
33581     {
33582         var d = this.dayField.validate();
33583         var m = this.monthField.validate();
33584         var y = this.yearField.validate();
33585         
33586         var valid = true;
33587         
33588         if(
33589                 (!this.dayAllowBlank && !d) ||
33590                 (!this.monthAllowBlank && !m) ||
33591                 (!this.yearAllowBlank && !y)
33592         ){
33593             valid = false;
33594         }
33595         
33596         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33597             return valid;
33598         }
33599         
33600         if(valid){
33601             this.markValid();
33602             return valid;
33603         }
33604         
33605         this.markInvalid();
33606         
33607         return valid;
33608     },
33609     
33610     markValid : function()
33611     {
33612         
33613         var label = this.el.select('label', true).first();
33614         var icon = this.el.select('i.fa-star', true).first();
33615
33616         if(label && icon){
33617             icon.remove();
33618         }
33619         
33620         this.fireEvent('valid', this);
33621     },
33622     
33623      /**
33624      * Mark this field as invalid
33625      * @param {String} msg The validation message
33626      */
33627     markInvalid : function(msg)
33628     {
33629         
33630         var label = this.el.select('label', true).first();
33631         var icon = this.el.select('i.fa-star', true).first();
33632
33633         if(label && !icon){
33634             this.el.select('.roo-date-split-field-label', true).createChild({
33635                 tag : 'i',
33636                 cls : 'text-danger fa fa-lg fa-star',
33637                 tooltip : 'This field is required',
33638                 style : 'margin-right:5px;'
33639             }, label, true);
33640         }
33641         
33642         this.fireEvent('invalid', this, msg);
33643     },
33644     
33645     clearInvalid : function()
33646     {
33647         var label = this.el.select('label', true).first();
33648         var icon = this.el.select('i.fa-star', true).first();
33649
33650         if(label && icon){
33651             icon.remove();
33652         }
33653         
33654         this.fireEvent('valid', this);
33655     },
33656     
33657     getName: function()
33658     {
33659         return this.name;
33660     }
33661     
33662 });
33663
33664  /**
33665  *
33666  * This is based on 
33667  * http://masonry.desandro.com
33668  *
33669  * The idea is to render all the bricks based on vertical width...
33670  *
33671  * The original code extends 'outlayer' - we might need to use that....
33672  * 
33673  */
33674
33675
33676 /**
33677  * @class Roo.bootstrap.LayoutMasonry
33678  * @extends Roo.bootstrap.Component
33679  * Bootstrap Layout Masonry class
33680  * 
33681  * @constructor
33682  * Create a new Element
33683  * @param {Object} config The config object
33684  */
33685
33686 Roo.bootstrap.LayoutMasonry = function(config){
33687     
33688     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33689     
33690     this.bricks = [];
33691     
33692     Roo.bootstrap.LayoutMasonry.register(this);
33693     
33694     this.addEvents({
33695         // raw events
33696         /**
33697          * @event layout
33698          * Fire after layout the items
33699          * @param {Roo.bootstrap.LayoutMasonry} this
33700          * @param {Roo.EventObject} e
33701          */
33702         "layout" : true
33703     });
33704     
33705 };
33706
33707 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33708     
33709     /**
33710      * @cfg {Boolean} isLayoutInstant = no animation?
33711      */   
33712     isLayoutInstant : false, // needed?
33713    
33714     /**
33715      * @cfg {Number} boxWidth  width of the columns
33716      */   
33717     boxWidth : 450,
33718     
33719       /**
33720      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33721      */   
33722     boxHeight : 0,
33723     
33724     /**
33725      * @cfg {Number} padWidth padding below box..
33726      */   
33727     padWidth : 10, 
33728     
33729     /**
33730      * @cfg {Number} gutter gutter width..
33731      */   
33732     gutter : 10,
33733     
33734      /**
33735      * @cfg {Number} maxCols maximum number of columns
33736      */   
33737     
33738     maxCols: 0,
33739     
33740     /**
33741      * @cfg {Boolean} isAutoInitial defalut true
33742      */   
33743     isAutoInitial : true, 
33744     
33745     containerWidth: 0,
33746     
33747     /**
33748      * @cfg {Boolean} isHorizontal defalut false
33749      */   
33750     isHorizontal : false, 
33751
33752     currentSize : null,
33753     
33754     tag: 'div',
33755     
33756     cls: '',
33757     
33758     bricks: null, //CompositeElement
33759     
33760     cols : 1,
33761     
33762     _isLayoutInited : false,
33763     
33764 //    isAlternative : false, // only use for vertical layout...
33765     
33766     /**
33767      * @cfg {Number} alternativePadWidth padding below box..
33768      */   
33769     alternativePadWidth : 50,
33770     
33771     selectedBrick : [],
33772     
33773     getAutoCreate : function(){
33774         
33775         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33776         
33777         var cfg = {
33778             tag: this.tag,
33779             cls: 'blog-masonary-wrapper ' + this.cls,
33780             cn : {
33781                 cls : 'mas-boxes masonary'
33782             }
33783         };
33784         
33785         return cfg;
33786     },
33787     
33788     getChildContainer: function( )
33789     {
33790         if (this.boxesEl) {
33791             return this.boxesEl;
33792         }
33793         
33794         this.boxesEl = this.el.select('.mas-boxes').first();
33795         
33796         return this.boxesEl;
33797     },
33798     
33799     
33800     initEvents : function()
33801     {
33802         var _this = this;
33803         
33804         if(this.isAutoInitial){
33805             Roo.log('hook children rendered');
33806             this.on('childrenrendered', function() {
33807                 Roo.log('children rendered');
33808                 _this.initial();
33809             } ,this);
33810         }
33811     },
33812     
33813     initial : function()
33814     {
33815         this.selectedBrick = [];
33816         
33817         this.currentSize = this.el.getBox(true);
33818         
33819         Roo.EventManager.onWindowResize(this.resize, this); 
33820
33821         if(!this.isAutoInitial){
33822             this.layout();
33823             return;
33824         }
33825         
33826         this.layout();
33827         
33828         return;
33829         //this.layout.defer(500,this);
33830         
33831     },
33832     
33833     resize : function()
33834     {
33835         var cs = this.el.getBox(true);
33836         
33837         if (
33838                 this.currentSize.width == cs.width && 
33839                 this.currentSize.x == cs.x && 
33840                 this.currentSize.height == cs.height && 
33841                 this.currentSize.y == cs.y 
33842         ) {
33843             Roo.log("no change in with or X or Y");
33844             return;
33845         }
33846         
33847         this.currentSize = cs;
33848         
33849         this.layout();
33850         
33851     },
33852     
33853     layout : function()
33854     {   
33855         this._resetLayout();
33856         
33857         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33858         
33859         this.layoutItems( isInstant );
33860       
33861         this._isLayoutInited = true;
33862         
33863         this.fireEvent('layout', this);
33864         
33865     },
33866     
33867     _resetLayout : function()
33868     {
33869         if(this.isHorizontal){
33870             this.horizontalMeasureColumns();
33871             return;
33872         }
33873         
33874         this.verticalMeasureColumns();
33875         
33876     },
33877     
33878     verticalMeasureColumns : function()
33879     {
33880         this.getContainerWidth();
33881         
33882 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33883 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33884 //            return;
33885 //        }
33886         
33887         var boxWidth = this.boxWidth + this.padWidth;
33888         
33889         if(this.containerWidth < this.boxWidth){
33890             boxWidth = this.containerWidth
33891         }
33892         
33893         var containerWidth = this.containerWidth;
33894         
33895         var cols = Math.floor(containerWidth / boxWidth);
33896         
33897         this.cols = Math.max( cols, 1 );
33898         
33899         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33900         
33901         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33902         
33903         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33904         
33905         this.colWidth = boxWidth + avail - this.padWidth;
33906         
33907         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33908         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33909     },
33910     
33911     horizontalMeasureColumns : function()
33912     {
33913         this.getContainerWidth();
33914         
33915         var boxWidth = this.boxWidth;
33916         
33917         if(this.containerWidth < boxWidth){
33918             boxWidth = this.containerWidth;
33919         }
33920         
33921         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33922         
33923         this.el.setHeight(boxWidth);
33924         
33925     },
33926     
33927     getContainerWidth : function()
33928     {
33929         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33930     },
33931     
33932     layoutItems : function( isInstant )
33933     {
33934         Roo.log(this.bricks);
33935         
33936         var items = Roo.apply([], this.bricks);
33937         
33938         if(this.isHorizontal){
33939             this._horizontalLayoutItems( items , isInstant );
33940             return;
33941         }
33942         
33943 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33944 //            this._verticalAlternativeLayoutItems( items , isInstant );
33945 //            return;
33946 //        }
33947         
33948         this._verticalLayoutItems( items , isInstant );
33949         
33950     },
33951     
33952     _verticalLayoutItems : function ( items , isInstant)
33953     {
33954         if ( !items || !items.length ) {
33955             return;
33956         }
33957         
33958         var standard = [
33959             ['xs', 'xs', 'xs', 'tall'],
33960             ['xs', 'xs', 'tall'],
33961             ['xs', 'xs', 'sm'],
33962             ['xs', 'xs', 'xs'],
33963             ['xs', 'tall'],
33964             ['xs', 'sm'],
33965             ['xs', 'xs'],
33966             ['xs'],
33967             
33968             ['sm', 'xs', 'xs'],
33969             ['sm', 'xs'],
33970             ['sm'],
33971             
33972             ['tall', 'xs', 'xs', 'xs'],
33973             ['tall', 'xs', 'xs'],
33974             ['tall', 'xs'],
33975             ['tall']
33976             
33977         ];
33978         
33979         var queue = [];
33980         
33981         var boxes = [];
33982         
33983         var box = [];
33984         
33985         Roo.each(items, function(item, k){
33986             
33987             switch (item.size) {
33988                 // these layouts take up a full box,
33989                 case 'md' :
33990                 case 'md-left' :
33991                 case 'md-right' :
33992                 case 'wide' :
33993                     
33994                     if(box.length){
33995                         boxes.push(box);
33996                         box = [];
33997                     }
33998                     
33999                     boxes.push([item]);
34000                     
34001                     break;
34002                     
34003                 case 'xs' :
34004                 case 'sm' :
34005                 case 'tall' :
34006                     
34007                     box.push(item);
34008                     
34009                     break;
34010                 default :
34011                     break;
34012                     
34013             }
34014             
34015         }, this);
34016         
34017         if(box.length){
34018             boxes.push(box);
34019             box = [];
34020         }
34021         
34022         var filterPattern = function(box, length)
34023         {
34024             if(!box.length){
34025                 return;
34026             }
34027             
34028             var match = false;
34029             
34030             var pattern = box.slice(0, length);
34031             
34032             var format = [];
34033             
34034             Roo.each(pattern, function(i){
34035                 format.push(i.size);
34036             }, this);
34037             
34038             Roo.each(standard, function(s){
34039                 
34040                 if(String(s) != String(format)){
34041                     return;
34042                 }
34043                 
34044                 match = true;
34045                 return false;
34046                 
34047             }, this);
34048             
34049             if(!match && length == 1){
34050                 return;
34051             }
34052             
34053             if(!match){
34054                 filterPattern(box, length - 1);
34055                 return;
34056             }
34057                 
34058             queue.push(pattern);
34059
34060             box = box.slice(length, box.length);
34061
34062             filterPattern(box, 4);
34063
34064             return;
34065             
34066         }
34067         
34068         Roo.each(boxes, function(box, k){
34069             
34070             if(!box.length){
34071                 return;
34072             }
34073             
34074             if(box.length == 1){
34075                 queue.push(box);
34076                 return;
34077             }
34078             
34079             filterPattern(box, 4);
34080             
34081         }, this);
34082         
34083         this._processVerticalLayoutQueue( queue, isInstant );
34084         
34085     },
34086     
34087 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34088 //    {
34089 //        if ( !items || !items.length ) {
34090 //            return;
34091 //        }
34092 //
34093 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34094 //        
34095 //    },
34096     
34097     _horizontalLayoutItems : function ( items , isInstant)
34098     {
34099         if ( !items || !items.length || items.length < 3) {
34100             return;
34101         }
34102         
34103         items.reverse();
34104         
34105         var eItems = items.slice(0, 3);
34106         
34107         items = items.slice(3, items.length);
34108         
34109         var standard = [
34110             ['xs', 'xs', 'xs', 'wide'],
34111             ['xs', 'xs', 'wide'],
34112             ['xs', 'xs', 'sm'],
34113             ['xs', 'xs', 'xs'],
34114             ['xs', 'wide'],
34115             ['xs', 'sm'],
34116             ['xs', 'xs'],
34117             ['xs'],
34118             
34119             ['sm', 'xs', 'xs'],
34120             ['sm', 'xs'],
34121             ['sm'],
34122             
34123             ['wide', 'xs', 'xs', 'xs'],
34124             ['wide', 'xs', 'xs'],
34125             ['wide', 'xs'],
34126             ['wide'],
34127             
34128             ['wide-thin']
34129         ];
34130         
34131         var queue = [];
34132         
34133         var boxes = [];
34134         
34135         var box = [];
34136         
34137         Roo.each(items, function(item, k){
34138             
34139             switch (item.size) {
34140                 case 'md' :
34141                 case 'md-left' :
34142                 case 'md-right' :
34143                 case 'tall' :
34144                     
34145                     if(box.length){
34146                         boxes.push(box);
34147                         box = [];
34148                     }
34149                     
34150                     boxes.push([item]);
34151                     
34152                     break;
34153                     
34154                 case 'xs' :
34155                 case 'sm' :
34156                 case 'wide' :
34157                 case 'wide-thin' :
34158                     
34159                     box.push(item);
34160                     
34161                     break;
34162                 default :
34163                     break;
34164                     
34165             }
34166             
34167         }, this);
34168         
34169         if(box.length){
34170             boxes.push(box);
34171             box = [];
34172         }
34173         
34174         var filterPattern = function(box, length)
34175         {
34176             if(!box.length){
34177                 return;
34178             }
34179             
34180             var match = false;
34181             
34182             var pattern = box.slice(0, length);
34183             
34184             var format = [];
34185             
34186             Roo.each(pattern, function(i){
34187                 format.push(i.size);
34188             }, this);
34189             
34190             Roo.each(standard, function(s){
34191                 
34192                 if(String(s) != String(format)){
34193                     return;
34194                 }
34195                 
34196                 match = true;
34197                 return false;
34198                 
34199             }, this);
34200             
34201             if(!match && length == 1){
34202                 return;
34203             }
34204             
34205             if(!match){
34206                 filterPattern(box, length - 1);
34207                 return;
34208             }
34209                 
34210             queue.push(pattern);
34211
34212             box = box.slice(length, box.length);
34213
34214             filterPattern(box, 4);
34215
34216             return;
34217             
34218         }
34219         
34220         Roo.each(boxes, function(box, k){
34221             
34222             if(!box.length){
34223                 return;
34224             }
34225             
34226             if(box.length == 1){
34227                 queue.push(box);
34228                 return;
34229             }
34230             
34231             filterPattern(box, 4);
34232             
34233         }, this);
34234         
34235         
34236         var prune = [];
34237         
34238         var pos = this.el.getBox(true);
34239         
34240         var minX = pos.x;
34241         
34242         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34243         
34244         var hit_end = false;
34245         
34246         Roo.each(queue, function(box){
34247             
34248             if(hit_end){
34249                 
34250                 Roo.each(box, function(b){
34251                 
34252                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34253                     b.el.hide();
34254
34255                 }, this);
34256
34257                 return;
34258             }
34259             
34260             var mx = 0;
34261             
34262             Roo.each(box, function(b){
34263                 
34264                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34265                 b.el.show();
34266
34267                 mx = Math.max(mx, b.x);
34268                 
34269             }, this);
34270             
34271             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34272             
34273             if(maxX < minX){
34274                 
34275                 Roo.each(box, function(b){
34276                 
34277                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34278                     b.el.hide();
34279                     
34280                 }, this);
34281                 
34282                 hit_end = true;
34283                 
34284                 return;
34285             }
34286             
34287             prune.push(box);
34288             
34289         }, this);
34290         
34291         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34292     },
34293     
34294     /** Sets position of item in DOM
34295     * @param {Element} item
34296     * @param {Number} x - horizontal position
34297     * @param {Number} y - vertical position
34298     * @param {Boolean} isInstant - disables transitions
34299     */
34300     _processVerticalLayoutQueue : function( queue, isInstant )
34301     {
34302         var pos = this.el.getBox(true);
34303         var x = pos.x;
34304         var y = pos.y;
34305         var maxY = [];
34306         
34307         for (var i = 0; i < this.cols; i++){
34308             maxY[i] = pos.y;
34309         }
34310         
34311         Roo.each(queue, function(box, k){
34312             
34313             var col = k % this.cols;
34314             
34315             Roo.each(box, function(b,kk){
34316                 
34317                 b.el.position('absolute');
34318                 
34319                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34320                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34321                 
34322                 if(b.size == 'md-left' || b.size == 'md-right'){
34323                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34324                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34325                 }
34326                 
34327                 b.el.setWidth(width);
34328                 b.el.setHeight(height);
34329                 // iframe?
34330                 b.el.select('iframe',true).setSize(width,height);
34331                 
34332             }, this);
34333             
34334             for (var i = 0; i < this.cols; i++){
34335                 
34336                 if(maxY[i] < maxY[col]){
34337                     col = i;
34338                     continue;
34339                 }
34340                 
34341                 col = Math.min(col, i);
34342                 
34343             }
34344             
34345             x = pos.x + col * (this.colWidth + this.padWidth);
34346             
34347             y = maxY[col];
34348             
34349             var positions = [];
34350             
34351             switch (box.length){
34352                 case 1 :
34353                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34354                     break;
34355                 case 2 :
34356                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34357                     break;
34358                 case 3 :
34359                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34360                     break;
34361                 case 4 :
34362                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34363                     break;
34364                 default :
34365                     break;
34366             }
34367             
34368             Roo.each(box, function(b,kk){
34369                 
34370                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34371                 
34372                 var sz = b.el.getSize();
34373                 
34374                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34375                 
34376             }, this);
34377             
34378         }, this);
34379         
34380         var mY = 0;
34381         
34382         for (var i = 0; i < this.cols; i++){
34383             mY = Math.max(mY, maxY[i]);
34384         }
34385         
34386         this.el.setHeight(mY - pos.y);
34387         
34388     },
34389     
34390 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34391 //    {
34392 //        var pos = this.el.getBox(true);
34393 //        var x = pos.x;
34394 //        var y = pos.y;
34395 //        var maxX = pos.right;
34396 //        
34397 //        var maxHeight = 0;
34398 //        
34399 //        Roo.each(items, function(item, k){
34400 //            
34401 //            var c = k % 2;
34402 //            
34403 //            item.el.position('absolute');
34404 //                
34405 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34406 //
34407 //            item.el.setWidth(width);
34408 //
34409 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34410 //
34411 //            item.el.setHeight(height);
34412 //            
34413 //            if(c == 0){
34414 //                item.el.setXY([x, y], isInstant ? false : true);
34415 //            } else {
34416 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34417 //            }
34418 //            
34419 //            y = y + height + this.alternativePadWidth;
34420 //            
34421 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34422 //            
34423 //        }, this);
34424 //        
34425 //        this.el.setHeight(maxHeight);
34426 //        
34427 //    },
34428     
34429     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34430     {
34431         var pos = this.el.getBox(true);
34432         
34433         var minX = pos.x;
34434         var minY = pos.y;
34435         
34436         var maxX = pos.right;
34437         
34438         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34439         
34440         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34441         
34442         Roo.each(queue, function(box, k){
34443             
34444             Roo.each(box, function(b, kk){
34445                 
34446                 b.el.position('absolute');
34447                 
34448                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34449                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34450                 
34451                 if(b.size == 'md-left' || b.size == 'md-right'){
34452                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34453                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34454                 }
34455                 
34456                 b.el.setWidth(width);
34457                 b.el.setHeight(height);
34458                 
34459             }, this);
34460             
34461             if(!box.length){
34462                 return;
34463             }
34464             
34465             var positions = [];
34466             
34467             switch (box.length){
34468                 case 1 :
34469                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34470                     break;
34471                 case 2 :
34472                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34473                     break;
34474                 case 3 :
34475                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34476                     break;
34477                 case 4 :
34478                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34479                     break;
34480                 default :
34481                     break;
34482             }
34483             
34484             Roo.each(box, function(b,kk){
34485                 
34486                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34487                 
34488                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34489                 
34490             }, this);
34491             
34492         }, this);
34493         
34494     },
34495     
34496     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34497     {
34498         Roo.each(eItems, function(b,k){
34499             
34500             b.size = (k == 0) ? 'sm' : 'xs';
34501             b.x = (k == 0) ? 2 : 1;
34502             b.y = (k == 0) ? 2 : 1;
34503             
34504             b.el.position('absolute');
34505             
34506             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34507                 
34508             b.el.setWidth(width);
34509             
34510             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34511             
34512             b.el.setHeight(height);
34513             
34514         }, this);
34515
34516         var positions = [];
34517         
34518         positions.push({
34519             x : maxX - this.unitWidth * 2 - this.gutter,
34520             y : minY
34521         });
34522         
34523         positions.push({
34524             x : maxX - this.unitWidth,
34525             y : minY + (this.unitWidth + this.gutter) * 2
34526         });
34527         
34528         positions.push({
34529             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34530             y : minY
34531         });
34532         
34533         Roo.each(eItems, function(b,k){
34534             
34535             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34536
34537         }, this);
34538         
34539     },
34540     
34541     getVerticalOneBoxColPositions : function(x, y, box)
34542     {
34543         var pos = [];
34544         
34545         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34546         
34547         if(box[0].size == 'md-left'){
34548             rand = 0;
34549         }
34550         
34551         if(box[0].size == 'md-right'){
34552             rand = 1;
34553         }
34554         
34555         pos.push({
34556             x : x + (this.unitWidth + this.gutter) * rand,
34557             y : y
34558         });
34559         
34560         return pos;
34561     },
34562     
34563     getVerticalTwoBoxColPositions : function(x, y, box)
34564     {
34565         var pos = [];
34566         
34567         if(box[0].size == 'xs'){
34568             
34569             pos.push({
34570                 x : x,
34571                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34572             });
34573
34574             pos.push({
34575                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34576                 y : y
34577             });
34578             
34579             return pos;
34580             
34581         }
34582         
34583         pos.push({
34584             x : x,
34585             y : y
34586         });
34587
34588         pos.push({
34589             x : x + (this.unitWidth + this.gutter) * 2,
34590             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34591         });
34592         
34593         return pos;
34594         
34595     },
34596     
34597     getVerticalThreeBoxColPositions : function(x, y, box)
34598     {
34599         var pos = [];
34600         
34601         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34602             
34603             pos.push({
34604                 x : x,
34605                 y : y
34606             });
34607
34608             pos.push({
34609                 x : x + (this.unitWidth + this.gutter) * 1,
34610                 y : y
34611             });
34612             
34613             pos.push({
34614                 x : x + (this.unitWidth + this.gutter) * 2,
34615                 y : y
34616             });
34617             
34618             return pos;
34619             
34620         }
34621         
34622         if(box[0].size == 'xs' && box[1].size == 'xs'){
34623             
34624             pos.push({
34625                 x : x,
34626                 y : y
34627             });
34628
34629             pos.push({
34630                 x : x,
34631                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34632             });
34633             
34634             pos.push({
34635                 x : x + (this.unitWidth + this.gutter) * 1,
34636                 y : y
34637             });
34638             
34639             return pos;
34640             
34641         }
34642         
34643         pos.push({
34644             x : x,
34645             y : y
34646         });
34647
34648         pos.push({
34649             x : x + (this.unitWidth + this.gutter) * 2,
34650             y : y
34651         });
34652
34653         pos.push({
34654             x : x + (this.unitWidth + this.gutter) * 2,
34655             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34656         });
34657             
34658         return pos;
34659         
34660     },
34661     
34662     getVerticalFourBoxColPositions : function(x, y, box)
34663     {
34664         var pos = [];
34665         
34666         if(box[0].size == 'xs'){
34667             
34668             pos.push({
34669                 x : x,
34670                 y : y
34671             });
34672
34673             pos.push({
34674                 x : x,
34675                 y : y + (this.unitHeight + this.gutter) * 1
34676             });
34677             
34678             pos.push({
34679                 x : x,
34680                 y : y + (this.unitHeight + this.gutter) * 2
34681             });
34682             
34683             pos.push({
34684                 x : x + (this.unitWidth + this.gutter) * 1,
34685                 y : y
34686             });
34687             
34688             return pos;
34689             
34690         }
34691         
34692         pos.push({
34693             x : x,
34694             y : y
34695         });
34696
34697         pos.push({
34698             x : x + (this.unitWidth + this.gutter) * 2,
34699             y : y
34700         });
34701
34702         pos.push({
34703             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34704             y : y + (this.unitHeight + this.gutter) * 1
34705         });
34706
34707         pos.push({
34708             x : x + (this.unitWidth + this.gutter) * 2,
34709             y : y + (this.unitWidth + this.gutter) * 2
34710         });
34711
34712         return pos;
34713         
34714     },
34715     
34716     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34717     {
34718         var pos = [];
34719         
34720         if(box[0].size == 'md-left'){
34721             pos.push({
34722                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34723                 y : minY
34724             });
34725             
34726             return pos;
34727         }
34728         
34729         if(box[0].size == 'md-right'){
34730             pos.push({
34731                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34732                 y : minY + (this.unitWidth + this.gutter) * 1
34733             });
34734             
34735             return pos;
34736         }
34737         
34738         var rand = Math.floor(Math.random() * (4 - box[0].y));
34739         
34740         pos.push({
34741             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34742             y : minY + (this.unitWidth + this.gutter) * rand
34743         });
34744         
34745         return pos;
34746         
34747     },
34748     
34749     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34750     {
34751         var pos = [];
34752         
34753         if(box[0].size == 'xs'){
34754             
34755             pos.push({
34756                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34757                 y : minY
34758             });
34759
34760             pos.push({
34761                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34762                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34763             });
34764             
34765             return pos;
34766             
34767         }
34768         
34769         pos.push({
34770             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34771             y : minY
34772         });
34773
34774         pos.push({
34775             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34776             y : minY + (this.unitWidth + this.gutter) * 2
34777         });
34778         
34779         return pos;
34780         
34781     },
34782     
34783     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34784     {
34785         var pos = [];
34786         
34787         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34788             
34789             pos.push({
34790                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34791                 y : minY
34792             });
34793
34794             pos.push({
34795                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34796                 y : minY + (this.unitWidth + this.gutter) * 1
34797             });
34798             
34799             pos.push({
34800                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34801                 y : minY + (this.unitWidth + this.gutter) * 2
34802             });
34803             
34804             return pos;
34805             
34806         }
34807         
34808         if(box[0].size == 'xs' && box[1].size == 'xs'){
34809             
34810             pos.push({
34811                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34812                 y : minY
34813             });
34814
34815             pos.push({
34816                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34817                 y : minY
34818             });
34819             
34820             pos.push({
34821                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34822                 y : minY + (this.unitWidth + this.gutter) * 1
34823             });
34824             
34825             return pos;
34826             
34827         }
34828         
34829         pos.push({
34830             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34831             y : minY
34832         });
34833
34834         pos.push({
34835             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34836             y : minY + (this.unitWidth + this.gutter) * 2
34837         });
34838
34839         pos.push({
34840             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34841             y : minY + (this.unitWidth + this.gutter) * 2
34842         });
34843             
34844         return pos;
34845         
34846     },
34847     
34848     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34849     {
34850         var pos = [];
34851         
34852         if(box[0].size == 'xs'){
34853             
34854             pos.push({
34855                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34856                 y : minY
34857             });
34858
34859             pos.push({
34860                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34861                 y : minY
34862             });
34863             
34864             pos.push({
34865                 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),
34866                 y : minY
34867             });
34868             
34869             pos.push({
34870                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34871                 y : minY + (this.unitWidth + this.gutter) * 1
34872             });
34873             
34874             return pos;
34875             
34876         }
34877         
34878         pos.push({
34879             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34880             y : minY
34881         });
34882         
34883         pos.push({
34884             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34885             y : minY + (this.unitWidth + this.gutter) * 2
34886         });
34887         
34888         pos.push({
34889             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34890             y : minY + (this.unitWidth + this.gutter) * 2
34891         });
34892         
34893         pos.push({
34894             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),
34895             y : minY + (this.unitWidth + this.gutter) * 2
34896         });
34897
34898         return pos;
34899         
34900     },
34901     
34902     /**
34903     * remove a Masonry Brick
34904     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34905     */
34906     removeBrick : function(brick_id)
34907     {
34908         if (!brick_id) {
34909             return;
34910         }
34911         
34912         for (var i = 0; i<this.bricks.length; i++) {
34913             if (this.bricks[i].id == brick_id) {
34914                 this.bricks.splice(i,1);
34915                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34916                 this.initial();
34917             }
34918         }
34919     },
34920     
34921     /**
34922     * adds a Masonry Brick
34923     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34924     */
34925     addBrick : function(cfg)
34926     {
34927         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34928         //this.register(cn);
34929         cn.parentId = this.id;
34930         cn.render(this.el);
34931         return cn;
34932     },
34933     
34934     /**
34935     * register a Masonry Brick
34936     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34937     */
34938     
34939     register : function(brick)
34940     {
34941         this.bricks.push(brick);
34942         brick.masonryId = this.id;
34943     },
34944     
34945     /**
34946     * clear all the Masonry Brick
34947     */
34948     clearAll : function()
34949     {
34950         this.bricks = [];
34951         //this.getChildContainer().dom.innerHTML = "";
34952         this.el.dom.innerHTML = '';
34953     },
34954     
34955     getSelected : function()
34956     {
34957         if (!this.selectedBrick) {
34958             return false;
34959         }
34960         
34961         return this.selectedBrick;
34962     }
34963 });
34964
34965 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34966     
34967     groups: {},
34968      /**
34969     * register a Masonry Layout
34970     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34971     */
34972     
34973     register : function(layout)
34974     {
34975         this.groups[layout.id] = layout;
34976     },
34977     /**
34978     * fetch a  Masonry Layout based on the masonry layout ID
34979     * @param {string} the masonry layout to add
34980     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34981     */
34982     
34983     get: function(layout_id) {
34984         if (typeof(this.groups[layout_id]) == 'undefined') {
34985             return false;
34986         }
34987         return this.groups[layout_id] ;
34988     }
34989     
34990     
34991     
34992 });
34993
34994  
34995
34996  /**
34997  *
34998  * This is based on 
34999  * http://masonry.desandro.com
35000  *
35001  * The idea is to render all the bricks based on vertical width...
35002  *
35003  * The original code extends 'outlayer' - we might need to use that....
35004  * 
35005  */
35006
35007
35008 /**
35009  * @class Roo.bootstrap.LayoutMasonryAuto
35010  * @extends Roo.bootstrap.Component
35011  * Bootstrap Layout Masonry class
35012  * 
35013  * @constructor
35014  * Create a new Element
35015  * @param {Object} config The config object
35016  */
35017
35018 Roo.bootstrap.LayoutMasonryAuto = function(config){
35019     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35020 };
35021
35022 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35023     
35024       /**
35025      * @cfg {Boolean} isFitWidth  - resize the width..
35026      */   
35027     isFitWidth : false,  // options..
35028     /**
35029      * @cfg {Boolean} isOriginLeft = left align?
35030      */   
35031     isOriginLeft : true,
35032     /**
35033      * @cfg {Boolean} isOriginTop = top align?
35034      */   
35035     isOriginTop : false,
35036     /**
35037      * @cfg {Boolean} isLayoutInstant = no animation?
35038      */   
35039     isLayoutInstant : false, // needed?
35040     /**
35041      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35042      */   
35043     isResizingContainer : true,
35044     /**
35045      * @cfg {Number} columnWidth  width of the columns 
35046      */   
35047     
35048     columnWidth : 0,
35049     
35050     /**
35051      * @cfg {Number} maxCols maximum number of columns
35052      */   
35053     
35054     maxCols: 0,
35055     /**
35056      * @cfg {Number} padHeight padding below box..
35057      */   
35058     
35059     padHeight : 10, 
35060     
35061     /**
35062      * @cfg {Boolean} isAutoInitial defalut true
35063      */   
35064     
35065     isAutoInitial : true, 
35066     
35067     // private?
35068     gutter : 0,
35069     
35070     containerWidth: 0,
35071     initialColumnWidth : 0,
35072     currentSize : null,
35073     
35074     colYs : null, // array.
35075     maxY : 0,
35076     padWidth: 10,
35077     
35078     
35079     tag: 'div',
35080     cls: '',
35081     bricks: null, //CompositeElement
35082     cols : 0, // array?
35083     // element : null, // wrapped now this.el
35084     _isLayoutInited : null, 
35085     
35086     
35087     getAutoCreate : function(){
35088         
35089         var cfg = {
35090             tag: this.tag,
35091             cls: 'blog-masonary-wrapper ' + this.cls,
35092             cn : {
35093                 cls : 'mas-boxes masonary'
35094             }
35095         };
35096         
35097         return cfg;
35098     },
35099     
35100     getChildContainer: function( )
35101     {
35102         if (this.boxesEl) {
35103             return this.boxesEl;
35104         }
35105         
35106         this.boxesEl = this.el.select('.mas-boxes').first();
35107         
35108         return this.boxesEl;
35109     },
35110     
35111     
35112     initEvents : function()
35113     {
35114         var _this = this;
35115         
35116         if(this.isAutoInitial){
35117             Roo.log('hook children rendered');
35118             this.on('childrenrendered', function() {
35119                 Roo.log('children rendered');
35120                 _this.initial();
35121             } ,this);
35122         }
35123         
35124     },
35125     
35126     initial : function()
35127     {
35128         this.reloadItems();
35129
35130         this.currentSize = this.el.getBox(true);
35131
35132         /// was window resize... - let's see if this works..
35133         Roo.EventManager.onWindowResize(this.resize, this); 
35134
35135         if(!this.isAutoInitial){
35136             this.layout();
35137             return;
35138         }
35139         
35140         this.layout.defer(500,this);
35141     },
35142     
35143     reloadItems: function()
35144     {
35145         this.bricks = this.el.select('.masonry-brick', true);
35146         
35147         this.bricks.each(function(b) {
35148             //Roo.log(b.getSize());
35149             if (!b.attr('originalwidth')) {
35150                 b.attr('originalwidth',  b.getSize().width);
35151             }
35152             
35153         });
35154         
35155         Roo.log(this.bricks.elements.length);
35156     },
35157     
35158     resize : function()
35159     {
35160         Roo.log('resize');
35161         var cs = this.el.getBox(true);
35162         
35163         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35164             Roo.log("no change in with or X");
35165             return;
35166         }
35167         this.currentSize = cs;
35168         this.layout();
35169     },
35170     
35171     layout : function()
35172     {
35173          Roo.log('layout');
35174         this._resetLayout();
35175         //this._manageStamps();
35176       
35177         // don't animate first layout
35178         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35179         this.layoutItems( isInstant );
35180       
35181         // flag for initalized
35182         this._isLayoutInited = true;
35183     },
35184     
35185     layoutItems : function( isInstant )
35186     {
35187         //var items = this._getItemsForLayout( this.items );
35188         // original code supports filtering layout items.. we just ignore it..
35189         
35190         this._layoutItems( this.bricks , isInstant );
35191       
35192         this._postLayout();
35193     },
35194     _layoutItems : function ( items , isInstant)
35195     {
35196        //this.fireEvent( 'layout', this, items );
35197     
35198
35199         if ( !items || !items.elements.length ) {
35200           // no items, emit event with empty array
35201             return;
35202         }
35203
35204         var queue = [];
35205         items.each(function(item) {
35206             Roo.log("layout item");
35207             Roo.log(item);
35208             // get x/y object from method
35209             var position = this._getItemLayoutPosition( item );
35210             // enqueue
35211             position.item = item;
35212             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35213             queue.push( position );
35214         }, this);
35215       
35216         this._processLayoutQueue( queue );
35217     },
35218     /** Sets position of item in DOM
35219     * @param {Element} item
35220     * @param {Number} x - horizontal position
35221     * @param {Number} y - vertical position
35222     * @param {Boolean} isInstant - disables transitions
35223     */
35224     _processLayoutQueue : function( queue )
35225     {
35226         for ( var i=0, len = queue.length; i < len; i++ ) {
35227             var obj = queue[i];
35228             obj.item.position('absolute');
35229             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35230         }
35231     },
35232       
35233     
35234     /**
35235     * Any logic you want to do after each layout,
35236     * i.e. size the container
35237     */
35238     _postLayout : function()
35239     {
35240         this.resizeContainer();
35241     },
35242     
35243     resizeContainer : function()
35244     {
35245         if ( !this.isResizingContainer ) {
35246             return;
35247         }
35248         var size = this._getContainerSize();
35249         if ( size ) {
35250             this.el.setSize(size.width,size.height);
35251             this.boxesEl.setSize(size.width,size.height);
35252         }
35253     },
35254     
35255     
35256     
35257     _resetLayout : function()
35258     {
35259         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35260         this.colWidth = this.el.getWidth();
35261         //this.gutter = this.el.getWidth(); 
35262         
35263         this.measureColumns();
35264
35265         // reset column Y
35266         var i = this.cols;
35267         this.colYs = [];
35268         while (i--) {
35269             this.colYs.push( 0 );
35270         }
35271     
35272         this.maxY = 0;
35273     },
35274
35275     measureColumns : function()
35276     {
35277         this.getContainerWidth();
35278       // if columnWidth is 0, default to outerWidth of first item
35279         if ( !this.columnWidth ) {
35280             var firstItem = this.bricks.first();
35281             Roo.log(firstItem);
35282             this.columnWidth  = this.containerWidth;
35283             if (firstItem && firstItem.attr('originalwidth') ) {
35284                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35285             }
35286             // columnWidth fall back to item of first element
35287             Roo.log("set column width?");
35288                         this.initialColumnWidth = this.columnWidth  ;
35289
35290             // if first elem has no width, default to size of container
35291             
35292         }
35293         
35294         
35295         if (this.initialColumnWidth) {
35296             this.columnWidth = this.initialColumnWidth;
35297         }
35298         
35299         
35300             
35301         // column width is fixed at the top - however if container width get's smaller we should
35302         // reduce it...
35303         
35304         // this bit calcs how man columns..
35305             
35306         var columnWidth = this.columnWidth += this.gutter;
35307       
35308         // calculate columns
35309         var containerWidth = this.containerWidth + this.gutter;
35310         
35311         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35312         // fix rounding errors, typically with gutters
35313         var excess = columnWidth - containerWidth % columnWidth;
35314         
35315         
35316         // if overshoot is less than a pixel, round up, otherwise floor it
35317         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35318         cols = Math[ mathMethod ]( cols );
35319         this.cols = Math.max( cols, 1 );
35320         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35321         
35322          // padding positioning..
35323         var totalColWidth = this.cols * this.columnWidth;
35324         var padavail = this.containerWidth - totalColWidth;
35325         // so for 2 columns - we need 3 'pads'
35326         
35327         var padNeeded = (1+this.cols) * this.padWidth;
35328         
35329         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35330         
35331         this.columnWidth += padExtra
35332         //this.padWidth = Math.floor(padavail /  ( this.cols));
35333         
35334         // adjust colum width so that padding is fixed??
35335         
35336         // we have 3 columns ... total = width * 3
35337         // we have X left over... that should be used by 
35338         
35339         //if (this.expandC) {
35340             
35341         //}
35342         
35343         
35344         
35345     },
35346     
35347     getContainerWidth : function()
35348     {
35349        /* // container is parent if fit width
35350         var container = this.isFitWidth ? this.element.parentNode : this.element;
35351         // check that this.size and size are there
35352         // IE8 triggers resize on body size change, so they might not be
35353         
35354         var size = getSize( container );  //FIXME
35355         this.containerWidth = size && size.innerWidth; //FIXME
35356         */
35357          
35358         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35359         
35360     },
35361     
35362     _getItemLayoutPosition : function( item )  // what is item?
35363     {
35364         // we resize the item to our columnWidth..
35365       
35366         item.setWidth(this.columnWidth);
35367         item.autoBoxAdjust  = false;
35368         
35369         var sz = item.getSize();
35370  
35371         // how many columns does this brick span
35372         var remainder = this.containerWidth % this.columnWidth;
35373         
35374         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35375         // round if off by 1 pixel, otherwise use ceil
35376         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35377         colSpan = Math.min( colSpan, this.cols );
35378         
35379         // normally this should be '1' as we dont' currently allow multi width columns..
35380         
35381         var colGroup = this._getColGroup( colSpan );
35382         // get the minimum Y value from the columns
35383         var minimumY = Math.min.apply( Math, colGroup );
35384         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35385         
35386         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35387          
35388         // position the brick
35389         var position = {
35390             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35391             y: this.currentSize.y + minimumY + this.padHeight
35392         };
35393         
35394         Roo.log(position);
35395         // apply setHeight to necessary columns
35396         var setHeight = minimumY + sz.height + this.padHeight;
35397         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35398         
35399         var setSpan = this.cols + 1 - colGroup.length;
35400         for ( var i = 0; i < setSpan; i++ ) {
35401           this.colYs[ shortColIndex + i ] = setHeight ;
35402         }
35403       
35404         return position;
35405     },
35406     
35407     /**
35408      * @param {Number} colSpan - number of columns the element spans
35409      * @returns {Array} colGroup
35410      */
35411     _getColGroup : function( colSpan )
35412     {
35413         if ( colSpan < 2 ) {
35414           // if brick spans only one column, use all the column Ys
35415           return this.colYs;
35416         }
35417       
35418         var colGroup = [];
35419         // how many different places could this brick fit horizontally
35420         var groupCount = this.cols + 1 - colSpan;
35421         // for each group potential horizontal position
35422         for ( var i = 0; i < groupCount; i++ ) {
35423           // make an array of colY values for that one group
35424           var groupColYs = this.colYs.slice( i, i + colSpan );
35425           // and get the max value of the array
35426           colGroup[i] = Math.max.apply( Math, groupColYs );
35427         }
35428         return colGroup;
35429     },
35430     /*
35431     _manageStamp : function( stamp )
35432     {
35433         var stampSize =  stamp.getSize();
35434         var offset = stamp.getBox();
35435         // get the columns that this stamp affects
35436         var firstX = this.isOriginLeft ? offset.x : offset.right;
35437         var lastX = firstX + stampSize.width;
35438         var firstCol = Math.floor( firstX / this.columnWidth );
35439         firstCol = Math.max( 0, firstCol );
35440         
35441         var lastCol = Math.floor( lastX / this.columnWidth );
35442         // lastCol should not go over if multiple of columnWidth #425
35443         lastCol -= lastX % this.columnWidth ? 0 : 1;
35444         lastCol = Math.min( this.cols - 1, lastCol );
35445         
35446         // set colYs to bottom of the stamp
35447         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35448             stampSize.height;
35449             
35450         for ( var i = firstCol; i <= lastCol; i++ ) {
35451           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35452         }
35453     },
35454     */
35455     
35456     _getContainerSize : function()
35457     {
35458         this.maxY = Math.max.apply( Math, this.colYs );
35459         var size = {
35460             height: this.maxY
35461         };
35462       
35463         if ( this.isFitWidth ) {
35464             size.width = this._getContainerFitWidth();
35465         }
35466       
35467         return size;
35468     },
35469     
35470     _getContainerFitWidth : function()
35471     {
35472         var unusedCols = 0;
35473         // count unused columns
35474         var i = this.cols;
35475         while ( --i ) {
35476           if ( this.colYs[i] !== 0 ) {
35477             break;
35478           }
35479           unusedCols++;
35480         }
35481         // fit container to columns that have been used
35482         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35483     },
35484     
35485     needsResizeLayout : function()
35486     {
35487         var previousWidth = this.containerWidth;
35488         this.getContainerWidth();
35489         return previousWidth !== this.containerWidth;
35490     }
35491  
35492 });
35493
35494  
35495
35496  /*
35497  * - LGPL
35498  *
35499  * element
35500  * 
35501  */
35502
35503 /**
35504  * @class Roo.bootstrap.MasonryBrick
35505  * @extends Roo.bootstrap.Component
35506  * Bootstrap MasonryBrick class
35507  * 
35508  * @constructor
35509  * Create a new MasonryBrick
35510  * @param {Object} config The config object
35511  */
35512
35513 Roo.bootstrap.MasonryBrick = function(config){
35514     
35515     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35516     
35517     Roo.bootstrap.MasonryBrick.register(this);
35518     
35519     this.addEvents({
35520         // raw events
35521         /**
35522          * @event click
35523          * When a MasonryBrick is clcik
35524          * @param {Roo.bootstrap.MasonryBrick} this
35525          * @param {Roo.EventObject} e
35526          */
35527         "click" : true
35528     });
35529 };
35530
35531 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35532     
35533     /**
35534      * @cfg {String} title
35535      */   
35536     title : '',
35537     /**
35538      * @cfg {String} html
35539      */   
35540     html : '',
35541     /**
35542      * @cfg {String} bgimage
35543      */   
35544     bgimage : '',
35545     /**
35546      * @cfg {String} videourl
35547      */   
35548     videourl : '',
35549     /**
35550      * @cfg {String} cls
35551      */   
35552     cls : '',
35553     /**
35554      * @cfg {String} href
35555      */   
35556     href : '',
35557     /**
35558      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35559      */   
35560     size : 'xs',
35561     
35562     /**
35563      * @cfg {String} placetitle (center|bottom)
35564      */   
35565     placetitle : '',
35566     
35567     /**
35568      * @cfg {Boolean} isFitContainer defalut true
35569      */   
35570     isFitContainer : true, 
35571     
35572     /**
35573      * @cfg {Boolean} preventDefault defalut false
35574      */   
35575     preventDefault : false, 
35576     
35577     /**
35578      * @cfg {Boolean} inverse defalut false
35579      */   
35580     maskInverse : false, 
35581     
35582     getAutoCreate : function()
35583     {
35584         if(!this.isFitContainer){
35585             return this.getSplitAutoCreate();
35586         }
35587         
35588         var cls = 'masonry-brick masonry-brick-full';
35589         
35590         if(this.href.length){
35591             cls += ' masonry-brick-link';
35592         }
35593         
35594         if(this.bgimage.length){
35595             cls += ' masonry-brick-image';
35596         }
35597         
35598         if(this.maskInverse){
35599             cls += ' mask-inverse';
35600         }
35601         
35602         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35603             cls += ' enable-mask';
35604         }
35605         
35606         if(this.size){
35607             cls += ' masonry-' + this.size + '-brick';
35608         }
35609         
35610         if(this.placetitle.length){
35611             
35612             switch (this.placetitle) {
35613                 case 'center' :
35614                     cls += ' masonry-center-title';
35615                     break;
35616                 case 'bottom' :
35617                     cls += ' masonry-bottom-title';
35618                     break;
35619                 default:
35620                     break;
35621             }
35622             
35623         } else {
35624             if(!this.html.length && !this.bgimage.length){
35625                 cls += ' masonry-center-title';
35626             }
35627
35628             if(!this.html.length && this.bgimage.length){
35629                 cls += ' masonry-bottom-title';
35630             }
35631         }
35632         
35633         if(this.cls){
35634             cls += ' ' + this.cls;
35635         }
35636         
35637         var cfg = {
35638             tag: (this.href.length) ? 'a' : 'div',
35639             cls: cls,
35640             cn: [
35641                 {
35642                     tag: 'div',
35643                     cls: 'masonry-brick-mask'
35644                 },
35645                 {
35646                     tag: 'div',
35647                     cls: 'masonry-brick-paragraph',
35648                     cn: []
35649                 }
35650             ]
35651         };
35652         
35653         if(this.href.length){
35654             cfg.href = this.href;
35655         }
35656         
35657         var cn = cfg.cn[1].cn;
35658         
35659         if(this.title.length){
35660             cn.push({
35661                 tag: 'h4',
35662                 cls: 'masonry-brick-title',
35663                 html: this.title
35664             });
35665         }
35666         
35667         if(this.html.length){
35668             cn.push({
35669                 tag: 'p',
35670                 cls: 'masonry-brick-text',
35671                 html: this.html
35672             });
35673         }
35674         
35675         if (!this.title.length && !this.html.length) {
35676             cfg.cn[1].cls += ' hide';
35677         }
35678         
35679         if(this.bgimage.length){
35680             cfg.cn.push({
35681                 tag: 'img',
35682                 cls: 'masonry-brick-image-view',
35683                 src: this.bgimage
35684             });
35685         }
35686         
35687         if(this.videourl.length){
35688             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35689             // youtube support only?
35690             cfg.cn.push({
35691                 tag: 'iframe',
35692                 cls: 'masonry-brick-image-view',
35693                 src: vurl,
35694                 frameborder : 0,
35695                 allowfullscreen : true
35696             });
35697         }
35698         
35699         return cfg;
35700         
35701     },
35702     
35703     getSplitAutoCreate : function()
35704     {
35705         var cls = 'masonry-brick masonry-brick-split';
35706         
35707         if(this.href.length){
35708             cls += ' masonry-brick-link';
35709         }
35710         
35711         if(this.bgimage.length){
35712             cls += ' masonry-brick-image';
35713         }
35714         
35715         if(this.size){
35716             cls += ' masonry-' + this.size + '-brick';
35717         }
35718         
35719         switch (this.placetitle) {
35720             case 'center' :
35721                 cls += ' masonry-center-title';
35722                 break;
35723             case 'bottom' :
35724                 cls += ' masonry-bottom-title';
35725                 break;
35726             default:
35727                 if(!this.bgimage.length){
35728                     cls += ' masonry-center-title';
35729                 }
35730
35731                 if(this.bgimage.length){
35732                     cls += ' masonry-bottom-title';
35733                 }
35734                 break;
35735         }
35736         
35737         if(this.cls){
35738             cls += ' ' + this.cls;
35739         }
35740         
35741         var cfg = {
35742             tag: (this.href.length) ? 'a' : 'div',
35743             cls: cls,
35744             cn: [
35745                 {
35746                     tag: 'div',
35747                     cls: 'masonry-brick-split-head',
35748                     cn: [
35749                         {
35750                             tag: 'div',
35751                             cls: 'masonry-brick-paragraph',
35752                             cn: []
35753                         }
35754                     ]
35755                 },
35756                 {
35757                     tag: 'div',
35758                     cls: 'masonry-brick-split-body',
35759                     cn: []
35760                 }
35761             ]
35762         };
35763         
35764         if(this.href.length){
35765             cfg.href = this.href;
35766         }
35767         
35768         if(this.title.length){
35769             cfg.cn[0].cn[0].cn.push({
35770                 tag: 'h4',
35771                 cls: 'masonry-brick-title',
35772                 html: this.title
35773             });
35774         }
35775         
35776         if(this.html.length){
35777             cfg.cn[1].cn.push({
35778                 tag: 'p',
35779                 cls: 'masonry-brick-text',
35780                 html: this.html
35781             });
35782         }
35783
35784         if(this.bgimage.length){
35785             cfg.cn[0].cn.push({
35786                 tag: 'img',
35787                 cls: 'masonry-brick-image-view',
35788                 src: this.bgimage
35789             });
35790         }
35791         
35792         if(this.videourl.length){
35793             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35794             // youtube support only?
35795             cfg.cn[0].cn.cn.push({
35796                 tag: 'iframe',
35797                 cls: 'masonry-brick-image-view',
35798                 src: vurl,
35799                 frameborder : 0,
35800                 allowfullscreen : true
35801             });
35802         }
35803         
35804         return cfg;
35805     },
35806     
35807     initEvents: function() 
35808     {
35809         switch (this.size) {
35810             case 'xs' :
35811                 this.x = 1;
35812                 this.y = 1;
35813                 break;
35814             case 'sm' :
35815                 this.x = 2;
35816                 this.y = 2;
35817                 break;
35818             case 'md' :
35819             case 'md-left' :
35820             case 'md-right' :
35821                 this.x = 3;
35822                 this.y = 3;
35823                 break;
35824             case 'tall' :
35825                 this.x = 2;
35826                 this.y = 3;
35827                 break;
35828             case 'wide' :
35829                 this.x = 3;
35830                 this.y = 2;
35831                 break;
35832             case 'wide-thin' :
35833                 this.x = 3;
35834                 this.y = 1;
35835                 break;
35836                         
35837             default :
35838                 break;
35839         }
35840         
35841         if(Roo.isTouch){
35842             this.el.on('touchstart', this.onTouchStart, this);
35843             this.el.on('touchmove', this.onTouchMove, this);
35844             this.el.on('touchend', this.onTouchEnd, this);
35845             this.el.on('contextmenu', this.onContextMenu, this);
35846         } else {
35847             this.el.on('mouseenter'  ,this.enter, this);
35848             this.el.on('mouseleave', this.leave, this);
35849             this.el.on('click', this.onClick, this);
35850         }
35851         
35852         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35853             this.parent().bricks.push(this);   
35854         }
35855         
35856     },
35857     
35858     onClick: function(e, el)
35859     {
35860         var time = this.endTimer - this.startTimer;
35861         // Roo.log(e.preventDefault());
35862         if(Roo.isTouch){
35863             if(time > 1000){
35864                 e.preventDefault();
35865                 return;
35866             }
35867         }
35868         
35869         if(!this.preventDefault){
35870             return;
35871         }
35872         
35873         e.preventDefault();
35874         
35875         if (this.activeClass != '') {
35876             this.selectBrick();
35877         }
35878         
35879         this.fireEvent('click', this, e);
35880     },
35881     
35882     enter: function(e, el)
35883     {
35884         e.preventDefault();
35885         
35886         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35887             return;
35888         }
35889         
35890         if(this.bgimage.length && this.html.length){
35891             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35892         }
35893     },
35894     
35895     leave: function(e, el)
35896     {
35897         e.preventDefault();
35898         
35899         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35900             return;
35901         }
35902         
35903         if(this.bgimage.length && this.html.length){
35904             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35905         }
35906     },
35907     
35908     onTouchStart: function(e, el)
35909     {
35910 //        e.preventDefault();
35911         
35912         this.touchmoved = false;
35913         
35914         if(!this.isFitContainer){
35915             return;
35916         }
35917         
35918         if(!this.bgimage.length || !this.html.length){
35919             return;
35920         }
35921         
35922         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35923         
35924         this.timer = new Date().getTime();
35925         
35926     },
35927     
35928     onTouchMove: function(e, el)
35929     {
35930         this.touchmoved = true;
35931     },
35932     
35933     onContextMenu : function(e,el)
35934     {
35935         e.preventDefault();
35936         e.stopPropagation();
35937         return false;
35938     },
35939     
35940     onTouchEnd: function(e, el)
35941     {
35942 //        e.preventDefault();
35943         
35944         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35945         
35946             this.leave(e,el);
35947             
35948             return;
35949         }
35950         
35951         if(!this.bgimage.length || !this.html.length){
35952             
35953             if(this.href.length){
35954                 window.location.href = this.href;
35955             }
35956             
35957             return;
35958         }
35959         
35960         if(!this.isFitContainer){
35961             return;
35962         }
35963         
35964         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35965         
35966         window.location.href = this.href;
35967     },
35968     
35969     //selection on single brick only
35970     selectBrick : function() {
35971         
35972         if (!this.parentId) {
35973             return;
35974         }
35975         
35976         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35977         var index = m.selectedBrick.indexOf(this.id);
35978         
35979         if ( index > -1) {
35980             m.selectedBrick.splice(index,1);
35981             this.el.removeClass(this.activeClass);
35982             return;
35983         }
35984         
35985         for(var i = 0; i < m.selectedBrick.length; i++) {
35986             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35987             b.el.removeClass(b.activeClass);
35988         }
35989         
35990         m.selectedBrick = [];
35991         
35992         m.selectedBrick.push(this.id);
35993         this.el.addClass(this.activeClass);
35994         return;
35995     },
35996     
35997     isSelected : function(){
35998         return this.el.hasClass(this.activeClass);
35999         
36000     }
36001 });
36002
36003 Roo.apply(Roo.bootstrap.MasonryBrick, {
36004     
36005     //groups: {},
36006     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36007      /**
36008     * register a Masonry Brick
36009     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36010     */
36011     
36012     register : function(brick)
36013     {
36014         //this.groups[brick.id] = brick;
36015         this.groups.add(brick.id, brick);
36016     },
36017     /**
36018     * fetch a  masonry brick based on the masonry brick ID
36019     * @param {string} the masonry brick to add
36020     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36021     */
36022     
36023     get: function(brick_id) 
36024     {
36025         // if (typeof(this.groups[brick_id]) == 'undefined') {
36026         //     return false;
36027         // }
36028         // return this.groups[brick_id] ;
36029         
36030         if(this.groups.key(brick_id)) {
36031             return this.groups.key(brick_id);
36032         }
36033         
36034         return false;
36035     }
36036     
36037     
36038     
36039 });
36040
36041  /*
36042  * - LGPL
36043  *
36044  * element
36045  * 
36046  */
36047
36048 /**
36049  * @class Roo.bootstrap.Brick
36050  * @extends Roo.bootstrap.Component
36051  * Bootstrap Brick class
36052  * 
36053  * @constructor
36054  * Create a new Brick
36055  * @param {Object} config The config object
36056  */
36057
36058 Roo.bootstrap.Brick = function(config){
36059     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36060     
36061     this.addEvents({
36062         // raw events
36063         /**
36064          * @event click
36065          * When a Brick is click
36066          * @param {Roo.bootstrap.Brick} this
36067          * @param {Roo.EventObject} e
36068          */
36069         "click" : true
36070     });
36071 };
36072
36073 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36074     
36075     /**
36076      * @cfg {String} title
36077      */   
36078     title : '',
36079     /**
36080      * @cfg {String} html
36081      */   
36082     html : '',
36083     /**
36084      * @cfg {String} bgimage
36085      */   
36086     bgimage : '',
36087     /**
36088      * @cfg {String} cls
36089      */   
36090     cls : '',
36091     /**
36092      * @cfg {String} href
36093      */   
36094     href : '',
36095     /**
36096      * @cfg {String} video
36097      */   
36098     video : '',
36099     /**
36100      * @cfg {Boolean} square
36101      */   
36102     square : true,
36103     
36104     getAutoCreate : function()
36105     {
36106         var cls = 'roo-brick';
36107         
36108         if(this.href.length){
36109             cls += ' roo-brick-link';
36110         }
36111         
36112         if(this.bgimage.length){
36113             cls += ' roo-brick-image';
36114         }
36115         
36116         if(!this.html.length && !this.bgimage.length){
36117             cls += ' roo-brick-center-title';
36118         }
36119         
36120         if(!this.html.length && this.bgimage.length){
36121             cls += ' roo-brick-bottom-title';
36122         }
36123         
36124         if(this.cls){
36125             cls += ' ' + this.cls;
36126         }
36127         
36128         var cfg = {
36129             tag: (this.href.length) ? 'a' : 'div',
36130             cls: cls,
36131             cn: [
36132                 {
36133                     tag: 'div',
36134                     cls: 'roo-brick-paragraph',
36135                     cn: []
36136                 }
36137             ]
36138         };
36139         
36140         if(this.href.length){
36141             cfg.href = this.href;
36142         }
36143         
36144         var cn = cfg.cn[0].cn;
36145         
36146         if(this.title.length){
36147             cn.push({
36148                 tag: 'h4',
36149                 cls: 'roo-brick-title',
36150                 html: this.title
36151             });
36152         }
36153         
36154         if(this.html.length){
36155             cn.push({
36156                 tag: 'p',
36157                 cls: 'roo-brick-text',
36158                 html: this.html
36159             });
36160         } else {
36161             cn.cls += ' hide';
36162         }
36163         
36164         if(this.bgimage.length){
36165             cfg.cn.push({
36166                 tag: 'img',
36167                 cls: 'roo-brick-image-view',
36168                 src: this.bgimage
36169             });
36170         }
36171         
36172         return cfg;
36173     },
36174     
36175     initEvents: function() 
36176     {
36177         if(this.title.length || this.html.length){
36178             this.el.on('mouseenter'  ,this.enter, this);
36179             this.el.on('mouseleave', this.leave, this);
36180         }
36181         
36182         Roo.EventManager.onWindowResize(this.resize, this); 
36183         
36184         if(this.bgimage.length){
36185             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36186             this.imageEl.on('load', this.onImageLoad, this);
36187             return;
36188         }
36189         
36190         this.resize();
36191     },
36192     
36193     onImageLoad : function()
36194     {
36195         this.resize();
36196     },
36197     
36198     resize : function()
36199     {
36200         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36201         
36202         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36203         
36204         if(this.bgimage.length){
36205             var image = this.el.select('.roo-brick-image-view', true).first();
36206             
36207             image.setWidth(paragraph.getWidth());
36208             
36209             if(this.square){
36210                 image.setHeight(paragraph.getWidth());
36211             }
36212             
36213             this.el.setHeight(image.getHeight());
36214             paragraph.setHeight(image.getHeight());
36215             
36216         }
36217         
36218     },
36219     
36220     enter: function(e, el)
36221     {
36222         e.preventDefault();
36223         
36224         if(this.bgimage.length){
36225             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36226             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36227         }
36228     },
36229     
36230     leave: function(e, el)
36231     {
36232         e.preventDefault();
36233         
36234         if(this.bgimage.length){
36235             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36236             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36237         }
36238     }
36239     
36240 });
36241
36242  
36243
36244  /*
36245  * - LGPL
36246  *
36247  * Number field 
36248  */
36249
36250 /**
36251  * @class Roo.bootstrap.NumberField
36252  * @extends Roo.bootstrap.Input
36253  * Bootstrap NumberField class
36254  * 
36255  * 
36256  * 
36257  * 
36258  * @constructor
36259  * Create a new NumberField
36260  * @param {Object} config The config object
36261  */
36262
36263 Roo.bootstrap.NumberField = function(config){
36264     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36265 };
36266
36267 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36268     
36269     /**
36270      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36271      */
36272     allowDecimals : true,
36273     /**
36274      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36275      */
36276     decimalSeparator : ".",
36277     /**
36278      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36279      */
36280     decimalPrecision : 2,
36281     /**
36282      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36283      */
36284     allowNegative : true,
36285     
36286     /**
36287      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36288      */
36289     allowZero: true,
36290     /**
36291      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36292      */
36293     minValue : Number.NEGATIVE_INFINITY,
36294     /**
36295      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36296      */
36297     maxValue : Number.MAX_VALUE,
36298     /**
36299      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36300      */
36301     minText : "The minimum value for this field is {0}",
36302     /**
36303      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36304      */
36305     maxText : "The maximum value for this field is {0}",
36306     /**
36307      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36308      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36309      */
36310     nanText : "{0} is not a valid number",
36311     /**
36312      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36313      */
36314     thousandsDelimiter : false,
36315     /**
36316      * @cfg {String} valueAlign alignment of value
36317      */
36318     valueAlign : "left",
36319
36320     getAutoCreate : function()
36321     {
36322         var hiddenInput = {
36323             tag: 'input',
36324             type: 'hidden',
36325             id: Roo.id(),
36326             cls: 'hidden-number-input'
36327         };
36328         
36329         if (this.name) {
36330             hiddenInput.name = this.name;
36331         }
36332         
36333         this.name = '';
36334         
36335         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36336         
36337         this.name = hiddenInput.name;
36338         
36339         if(cfg.cn.length > 0) {
36340             cfg.cn.push(hiddenInput);
36341         }
36342         
36343         return cfg;
36344     },
36345
36346     // private
36347     initEvents : function()
36348     {   
36349         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36350         
36351         var allowed = "0123456789";
36352         
36353         if(this.allowDecimals){
36354             allowed += this.decimalSeparator;
36355         }
36356         
36357         if(this.allowNegative){
36358             allowed += "-";
36359         }
36360         
36361         if(this.thousandsDelimiter) {
36362             allowed += ",";
36363         }
36364         
36365         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36366         
36367         var keyPress = function(e){
36368             
36369             var k = e.getKey();
36370             
36371             var c = e.getCharCode();
36372             
36373             if(
36374                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36375                     allowed.indexOf(String.fromCharCode(c)) === -1
36376             ){
36377                 e.stopEvent();
36378                 return;
36379             }
36380             
36381             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36382                 return;
36383             }
36384             
36385             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36386                 e.stopEvent();
36387             }
36388         };
36389         
36390         this.el.on("keypress", keyPress, this);
36391     },
36392     
36393     validateValue : function(value)
36394     {
36395         
36396         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36397             return false;
36398         }
36399         
36400         var num = this.parseValue(value);
36401         
36402         if(isNaN(num)){
36403             this.markInvalid(String.format(this.nanText, value));
36404             return false;
36405         }
36406         
36407         if(num < this.minValue){
36408             this.markInvalid(String.format(this.minText, this.minValue));
36409             return false;
36410         }
36411         
36412         if(num > this.maxValue){
36413             this.markInvalid(String.format(this.maxText, this.maxValue));
36414             return false;
36415         }
36416         
36417         return true;
36418     },
36419
36420     getValue : function()
36421     {
36422         var v = this.hiddenEl().getValue();
36423         
36424         return this.fixPrecision(this.parseValue(v));
36425     },
36426
36427     parseValue : function(value)
36428     {
36429         if(this.thousandsDelimiter) {
36430             value += "";
36431             r = new RegExp(",", "g");
36432             value = value.replace(r, "");
36433         }
36434         
36435         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36436         return isNaN(value) ? '' : value;
36437     },
36438
36439     fixPrecision : function(value)
36440     {
36441         if(this.thousandsDelimiter) {
36442             value += "";
36443             r = new RegExp(",", "g");
36444             value = value.replace(r, "");
36445         }
36446         
36447         var nan = isNaN(value);
36448         
36449         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36450             return nan ? '' : value;
36451         }
36452         return parseFloat(value).toFixed(this.decimalPrecision);
36453     },
36454
36455     setValue : function(v)
36456     {
36457         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36458         
36459         this.value = v;
36460         
36461         if(this.rendered){
36462             
36463             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36464             
36465             this.inputEl().dom.value = (v == '') ? '' :
36466                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36467             
36468             if(!this.allowZero && v === '0') {
36469                 this.hiddenEl().dom.value = '';
36470                 this.inputEl().dom.value = '';
36471             }
36472             
36473             this.validate();
36474         }
36475     },
36476
36477     decimalPrecisionFcn : function(v)
36478     {
36479         return Math.floor(v);
36480     },
36481
36482     beforeBlur : function()
36483     {
36484         var v = this.parseValue(this.getRawValue());
36485         
36486         if(v || v === 0 || v === ''){
36487             this.setValue(v);
36488         }
36489     },
36490     
36491     hiddenEl : function()
36492     {
36493         return this.el.select('input.hidden-number-input',true).first();
36494     }
36495     
36496 });
36497
36498  
36499
36500 /*
36501 * Licence: LGPL
36502 */
36503
36504 /**
36505  * @class Roo.bootstrap.DocumentSlider
36506  * @extends Roo.bootstrap.Component
36507  * Bootstrap DocumentSlider class
36508  * 
36509  * @constructor
36510  * Create a new DocumentViewer
36511  * @param {Object} config The config object
36512  */
36513
36514 Roo.bootstrap.DocumentSlider = function(config){
36515     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36516     
36517     this.files = [];
36518     
36519     this.addEvents({
36520         /**
36521          * @event initial
36522          * Fire after initEvent
36523          * @param {Roo.bootstrap.DocumentSlider} this
36524          */
36525         "initial" : true,
36526         /**
36527          * @event update
36528          * Fire after update
36529          * @param {Roo.bootstrap.DocumentSlider} this
36530          */
36531         "update" : true,
36532         /**
36533          * @event click
36534          * Fire after click
36535          * @param {Roo.bootstrap.DocumentSlider} this
36536          */
36537         "click" : true
36538     });
36539 };
36540
36541 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36542     
36543     files : false,
36544     
36545     indicator : 0,
36546     
36547     getAutoCreate : function()
36548     {
36549         var cfg = {
36550             tag : 'div',
36551             cls : 'roo-document-slider',
36552             cn : [
36553                 {
36554                     tag : 'div',
36555                     cls : 'roo-document-slider-header',
36556                     cn : [
36557                         {
36558                             tag : 'div',
36559                             cls : 'roo-document-slider-header-title'
36560                         }
36561                     ]
36562                 },
36563                 {
36564                     tag : 'div',
36565                     cls : 'roo-document-slider-body',
36566                     cn : [
36567                         {
36568                             tag : 'div',
36569                             cls : 'roo-document-slider-prev',
36570                             cn : [
36571                                 {
36572                                     tag : 'i',
36573                                     cls : 'fa fa-chevron-left'
36574                                 }
36575                             ]
36576                         },
36577                         {
36578                             tag : 'div',
36579                             cls : 'roo-document-slider-thumb',
36580                             cn : [
36581                                 {
36582                                     tag : 'img',
36583                                     cls : 'roo-document-slider-image'
36584                                 }
36585                             ]
36586                         },
36587                         {
36588                             tag : 'div',
36589                             cls : 'roo-document-slider-next',
36590                             cn : [
36591                                 {
36592                                     tag : 'i',
36593                                     cls : 'fa fa-chevron-right'
36594                                 }
36595                             ]
36596                         }
36597                     ]
36598                 }
36599             ]
36600         };
36601         
36602         return cfg;
36603     },
36604     
36605     initEvents : function()
36606     {
36607         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36608         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36609         
36610         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36611         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36612         
36613         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36614         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36615         
36616         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36617         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36618         
36619         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36620         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36621         
36622         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36623         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36624         
36625         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36626         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36627         
36628         this.thumbEl.on('click', this.onClick, this);
36629         
36630         this.prevIndicator.on('click', this.prev, this);
36631         
36632         this.nextIndicator.on('click', this.next, this);
36633         
36634     },
36635     
36636     initial : function()
36637     {
36638         if(this.files.length){
36639             this.indicator = 1;
36640             this.update()
36641         }
36642         
36643         this.fireEvent('initial', this);
36644     },
36645     
36646     update : function()
36647     {
36648         this.imageEl.attr('src', this.files[this.indicator - 1]);
36649         
36650         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36651         
36652         this.prevIndicator.show();
36653         
36654         if(this.indicator == 1){
36655             this.prevIndicator.hide();
36656         }
36657         
36658         this.nextIndicator.show();
36659         
36660         if(this.indicator == this.files.length){
36661             this.nextIndicator.hide();
36662         }
36663         
36664         this.thumbEl.scrollTo('top');
36665         
36666         this.fireEvent('update', this);
36667     },
36668     
36669     onClick : function(e)
36670     {
36671         e.preventDefault();
36672         
36673         this.fireEvent('click', this);
36674     },
36675     
36676     prev : function(e)
36677     {
36678         e.preventDefault();
36679         
36680         this.indicator = Math.max(1, this.indicator - 1);
36681         
36682         this.update();
36683     },
36684     
36685     next : function(e)
36686     {
36687         e.preventDefault();
36688         
36689         this.indicator = Math.min(this.files.length, this.indicator + 1);
36690         
36691         this.update();
36692     }
36693 });
36694 /*
36695  * - LGPL
36696  *
36697  * RadioSet
36698  *
36699  *
36700  */
36701
36702 /**
36703  * @class Roo.bootstrap.RadioSet
36704  * @extends Roo.bootstrap.Input
36705  * Bootstrap RadioSet class
36706  * @cfg {String} indicatorpos (left|right) default left
36707  * @cfg {Boolean} inline (true|false) inline the element (default true)
36708  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36709  * @constructor
36710  * Create a new RadioSet
36711  * @param {Object} config The config object
36712  */
36713
36714 Roo.bootstrap.RadioSet = function(config){
36715     
36716     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36717     
36718     this.radioes = [];
36719     
36720     Roo.bootstrap.RadioSet.register(this);
36721     
36722     this.addEvents({
36723         /**
36724         * @event check
36725         * Fires when the element is checked or unchecked.
36726         * @param {Roo.bootstrap.RadioSet} this This radio
36727         * @param {Roo.bootstrap.Radio} item The checked item
36728         */
36729        check : true,
36730        /**
36731         * @event click
36732         * Fires when the element is click.
36733         * @param {Roo.bootstrap.RadioSet} this This radio set
36734         * @param {Roo.bootstrap.Radio} item The checked item
36735         * @param {Roo.EventObject} e The event object
36736         */
36737        click : true
36738     });
36739     
36740 };
36741
36742 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36743
36744     radioes : false,
36745     
36746     inline : true,
36747     
36748     weight : '',
36749     
36750     indicatorpos : 'left',
36751     
36752     getAutoCreate : function()
36753     {
36754         var label = {
36755             tag : 'label',
36756             cls : 'roo-radio-set-label',
36757             cn : [
36758                 {
36759                     tag : 'span',
36760                     html : this.fieldLabel
36761                 }
36762             ]
36763         };
36764         if (Roo.bootstrap.version == 3) {
36765             
36766             
36767             if(this.indicatorpos == 'left'){
36768                 label.cn.unshift({
36769                     tag : 'i',
36770                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36771                     tooltip : 'This field is required'
36772                 });
36773             } else {
36774                 label.cn.push({
36775                     tag : 'i',
36776                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36777                     tooltip : 'This field is required'
36778                 });
36779             }
36780         }
36781         var items = {
36782             tag : 'div',
36783             cls : 'roo-radio-set-items'
36784         };
36785         
36786         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36787         
36788         if (align === 'left' && this.fieldLabel.length) {
36789             
36790             items = {
36791                 cls : "roo-radio-set-right", 
36792                 cn: [
36793                     items
36794                 ]
36795             };
36796             
36797             if(this.labelWidth > 12){
36798                 label.style = "width: " + this.labelWidth + 'px';
36799             }
36800             
36801             if(this.labelWidth < 13 && this.labelmd == 0){
36802                 this.labelmd = this.labelWidth;
36803             }
36804             
36805             if(this.labellg > 0){
36806                 label.cls += ' col-lg-' + this.labellg;
36807                 items.cls += ' col-lg-' + (12 - this.labellg);
36808             }
36809             
36810             if(this.labelmd > 0){
36811                 label.cls += ' col-md-' + this.labelmd;
36812                 items.cls += ' col-md-' + (12 - this.labelmd);
36813             }
36814             
36815             if(this.labelsm > 0){
36816                 label.cls += ' col-sm-' + this.labelsm;
36817                 items.cls += ' col-sm-' + (12 - this.labelsm);
36818             }
36819             
36820             if(this.labelxs > 0){
36821                 label.cls += ' col-xs-' + this.labelxs;
36822                 items.cls += ' col-xs-' + (12 - this.labelxs);
36823             }
36824         }
36825         
36826         var cfg = {
36827             tag : 'div',
36828             cls : 'roo-radio-set',
36829             cn : [
36830                 {
36831                     tag : 'input',
36832                     cls : 'roo-radio-set-input',
36833                     type : 'hidden',
36834                     name : this.name,
36835                     value : this.value ? this.value :  ''
36836                 },
36837                 label,
36838                 items
36839             ]
36840         };
36841         
36842         if(this.weight.length){
36843             cfg.cls += ' roo-radio-' + this.weight;
36844         }
36845         
36846         if(this.inline) {
36847             cfg.cls += ' roo-radio-set-inline';
36848         }
36849         
36850         var settings=this;
36851         ['xs','sm','md','lg'].map(function(size){
36852             if (settings[size]) {
36853                 cfg.cls += ' col-' + size + '-' + settings[size];
36854             }
36855         });
36856         
36857         return cfg;
36858         
36859     },
36860
36861     initEvents : function()
36862     {
36863         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36864         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36865         
36866         if(!this.fieldLabel.length){
36867             this.labelEl.hide();
36868         }
36869         
36870         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36871         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36872         
36873         this.indicator = this.indicatorEl();
36874         
36875         if(this.indicator){
36876             this.indicator.addClass('invisible');
36877         }
36878         
36879         this.originalValue = this.getValue();
36880         
36881     },
36882     
36883     inputEl: function ()
36884     {
36885         return this.el.select('.roo-radio-set-input', true).first();
36886     },
36887     
36888     getChildContainer : function()
36889     {
36890         return this.itemsEl;
36891     },
36892     
36893     register : function(item)
36894     {
36895         this.radioes.push(item);
36896         
36897     },
36898     
36899     validate : function()
36900     {   
36901         if(this.getVisibilityEl().hasClass('hidden')){
36902             return true;
36903         }
36904         
36905         var valid = false;
36906         
36907         Roo.each(this.radioes, function(i){
36908             if(!i.checked){
36909                 return;
36910             }
36911             
36912             valid = true;
36913             return false;
36914         });
36915         
36916         if(this.allowBlank) {
36917             return true;
36918         }
36919         
36920         if(this.disabled || valid){
36921             this.markValid();
36922             return true;
36923         }
36924         
36925         this.markInvalid();
36926         return false;
36927         
36928     },
36929     
36930     markValid : function()
36931     {
36932         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36933             this.indicatorEl().removeClass('visible');
36934             this.indicatorEl().addClass('invisible');
36935         }
36936         
36937         
36938         if (Roo.bootstrap.version == 3) {
36939             this.el.removeClass([this.invalidClass, this.validClass]);
36940             this.el.addClass(this.validClass);
36941         } else {
36942             this.el.removeClass(['is-invalid','is-valid']);
36943             this.el.addClass(['is-valid']);
36944         }
36945         this.fireEvent('valid', this);
36946     },
36947     
36948     markInvalid : function(msg)
36949     {
36950         if(this.allowBlank || this.disabled){
36951             return;
36952         }
36953         
36954         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36955             this.indicatorEl().removeClass('invisible');
36956             this.indicatorEl().addClass('visible');
36957         }
36958         if (Roo.bootstrap.version == 3) {
36959             this.el.removeClass([this.invalidClass, this.validClass]);
36960             this.el.addClass(this.invalidClass);
36961         } else {
36962             this.el.removeClass(['is-invalid','is-valid']);
36963             this.el.addClass(['is-invalid']);
36964         }
36965         
36966         this.fireEvent('invalid', this, msg);
36967         
36968     },
36969     
36970     setValue : function(v, suppressEvent)
36971     {   
36972         if(this.value === v){
36973             return;
36974         }
36975         
36976         this.value = v;
36977         
36978         if(this.rendered){
36979             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36980         }
36981         
36982         Roo.each(this.radioes, function(i){
36983             i.checked = false;
36984             i.el.removeClass('checked');
36985         });
36986         
36987         Roo.each(this.radioes, function(i){
36988             
36989             if(i.value === v || i.value.toString() === v.toString()){
36990                 i.checked = true;
36991                 i.el.addClass('checked');
36992                 
36993                 if(suppressEvent !== true){
36994                     this.fireEvent('check', this, i);
36995                 }
36996                 
36997                 return false;
36998             }
36999             
37000         }, this);
37001         
37002         this.validate();
37003     },
37004     
37005     clearInvalid : function(){
37006         
37007         if(!this.el || this.preventMark){
37008             return;
37009         }
37010         
37011         this.el.removeClass([this.invalidClass]);
37012         
37013         this.fireEvent('valid', this);
37014     }
37015     
37016 });
37017
37018 Roo.apply(Roo.bootstrap.RadioSet, {
37019     
37020     groups: {},
37021     
37022     register : function(set)
37023     {
37024         this.groups[set.name] = set;
37025     },
37026     
37027     get: function(name) 
37028     {
37029         if (typeof(this.groups[name]) == 'undefined') {
37030             return false;
37031         }
37032         
37033         return this.groups[name] ;
37034     }
37035     
37036 });
37037 /*
37038  * Based on:
37039  * Ext JS Library 1.1.1
37040  * Copyright(c) 2006-2007, Ext JS, LLC.
37041  *
37042  * Originally Released Under LGPL - original licence link has changed is not relivant.
37043  *
37044  * Fork - LGPL
37045  * <script type="text/javascript">
37046  */
37047
37048
37049 /**
37050  * @class Roo.bootstrap.SplitBar
37051  * @extends Roo.util.Observable
37052  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37053  * <br><br>
37054  * Usage:
37055  * <pre><code>
37056 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37057                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37058 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37059 split.minSize = 100;
37060 split.maxSize = 600;
37061 split.animate = true;
37062 split.on('moved', splitterMoved);
37063 </code></pre>
37064  * @constructor
37065  * Create a new SplitBar
37066  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37067  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37068  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37069  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37070                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37071                         position of the SplitBar).
37072  */
37073 Roo.bootstrap.SplitBar = function(cfg){
37074     
37075     /** @private */
37076     
37077     //{
37078     //  dragElement : elm
37079     //  resizingElement: el,
37080         // optional..
37081     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37082     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37083         // existingProxy ???
37084     //}
37085     
37086     this.el = Roo.get(cfg.dragElement, true);
37087     this.el.dom.unselectable = "on";
37088     /** @private */
37089     this.resizingEl = Roo.get(cfg.resizingElement, true);
37090
37091     /**
37092      * @private
37093      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37094      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37095      * @type Number
37096      */
37097     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37098     
37099     /**
37100      * The minimum size of the resizing element. (Defaults to 0)
37101      * @type Number
37102      */
37103     this.minSize = 0;
37104     
37105     /**
37106      * The maximum size of the resizing element. (Defaults to 2000)
37107      * @type Number
37108      */
37109     this.maxSize = 2000;
37110     
37111     /**
37112      * Whether to animate the transition to the new size
37113      * @type Boolean
37114      */
37115     this.animate = false;
37116     
37117     /**
37118      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37119      * @type Boolean
37120      */
37121     this.useShim = false;
37122     
37123     /** @private */
37124     this.shim = null;
37125     
37126     if(!cfg.existingProxy){
37127         /** @private */
37128         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37129     }else{
37130         this.proxy = Roo.get(cfg.existingProxy).dom;
37131     }
37132     /** @private */
37133     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37134     
37135     /** @private */
37136     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37137     
37138     /** @private */
37139     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37140     
37141     /** @private */
37142     this.dragSpecs = {};
37143     
37144     /**
37145      * @private The adapter to use to positon and resize elements
37146      */
37147     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37148     this.adapter.init(this);
37149     
37150     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37151         /** @private */
37152         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37153         this.el.addClass("roo-splitbar-h");
37154     }else{
37155         /** @private */
37156         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37157         this.el.addClass("roo-splitbar-v");
37158     }
37159     
37160     this.addEvents({
37161         /**
37162          * @event resize
37163          * Fires when the splitter is moved (alias for {@link #event-moved})
37164          * @param {Roo.bootstrap.SplitBar} this
37165          * @param {Number} newSize the new width or height
37166          */
37167         "resize" : true,
37168         /**
37169          * @event moved
37170          * Fires when the splitter is moved
37171          * @param {Roo.bootstrap.SplitBar} this
37172          * @param {Number} newSize the new width or height
37173          */
37174         "moved" : true,
37175         /**
37176          * @event beforeresize
37177          * Fires before the splitter is dragged
37178          * @param {Roo.bootstrap.SplitBar} this
37179          */
37180         "beforeresize" : true,
37181
37182         "beforeapply" : true
37183     });
37184
37185     Roo.util.Observable.call(this);
37186 };
37187
37188 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37189     onStartProxyDrag : function(x, y){
37190         this.fireEvent("beforeresize", this);
37191         if(!this.overlay){
37192             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37193             o.unselectable();
37194             o.enableDisplayMode("block");
37195             // all splitbars share the same overlay
37196             Roo.bootstrap.SplitBar.prototype.overlay = o;
37197         }
37198         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37199         this.overlay.show();
37200         Roo.get(this.proxy).setDisplayed("block");
37201         var size = this.adapter.getElementSize(this);
37202         this.activeMinSize = this.getMinimumSize();;
37203         this.activeMaxSize = this.getMaximumSize();;
37204         var c1 = size - this.activeMinSize;
37205         var c2 = Math.max(this.activeMaxSize - size, 0);
37206         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37207             this.dd.resetConstraints();
37208             this.dd.setXConstraint(
37209                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37210                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37211             );
37212             this.dd.setYConstraint(0, 0);
37213         }else{
37214             this.dd.resetConstraints();
37215             this.dd.setXConstraint(0, 0);
37216             this.dd.setYConstraint(
37217                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37218                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37219             );
37220          }
37221         this.dragSpecs.startSize = size;
37222         this.dragSpecs.startPoint = [x, y];
37223         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37224     },
37225     
37226     /** 
37227      * @private Called after the drag operation by the DDProxy
37228      */
37229     onEndProxyDrag : function(e){
37230         Roo.get(this.proxy).setDisplayed(false);
37231         var endPoint = Roo.lib.Event.getXY(e);
37232         if(this.overlay){
37233             this.overlay.hide();
37234         }
37235         var newSize;
37236         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37237             newSize = this.dragSpecs.startSize + 
37238                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37239                     endPoint[0] - this.dragSpecs.startPoint[0] :
37240                     this.dragSpecs.startPoint[0] - endPoint[0]
37241                 );
37242         }else{
37243             newSize = this.dragSpecs.startSize + 
37244                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37245                     endPoint[1] - this.dragSpecs.startPoint[1] :
37246                     this.dragSpecs.startPoint[1] - endPoint[1]
37247                 );
37248         }
37249         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37250         if(newSize != this.dragSpecs.startSize){
37251             if(this.fireEvent('beforeapply', this, newSize) !== false){
37252                 this.adapter.setElementSize(this, newSize);
37253                 this.fireEvent("moved", this, newSize);
37254                 this.fireEvent("resize", this, newSize);
37255             }
37256         }
37257     },
37258     
37259     /**
37260      * Get the adapter this SplitBar uses
37261      * @return The adapter object
37262      */
37263     getAdapter : function(){
37264         return this.adapter;
37265     },
37266     
37267     /**
37268      * Set the adapter this SplitBar uses
37269      * @param {Object} adapter A SplitBar adapter object
37270      */
37271     setAdapter : function(adapter){
37272         this.adapter = adapter;
37273         this.adapter.init(this);
37274     },
37275     
37276     /**
37277      * Gets the minimum size for the resizing element
37278      * @return {Number} The minimum size
37279      */
37280     getMinimumSize : function(){
37281         return this.minSize;
37282     },
37283     
37284     /**
37285      * Sets the minimum size for the resizing element
37286      * @param {Number} minSize The minimum size
37287      */
37288     setMinimumSize : function(minSize){
37289         this.minSize = minSize;
37290     },
37291     
37292     /**
37293      * Gets the maximum size for the resizing element
37294      * @return {Number} The maximum size
37295      */
37296     getMaximumSize : function(){
37297         return this.maxSize;
37298     },
37299     
37300     /**
37301      * Sets the maximum size for the resizing element
37302      * @param {Number} maxSize The maximum size
37303      */
37304     setMaximumSize : function(maxSize){
37305         this.maxSize = maxSize;
37306     },
37307     
37308     /**
37309      * Sets the initialize size for the resizing element
37310      * @param {Number} size The initial size
37311      */
37312     setCurrentSize : function(size){
37313         var oldAnimate = this.animate;
37314         this.animate = false;
37315         this.adapter.setElementSize(this, size);
37316         this.animate = oldAnimate;
37317     },
37318     
37319     /**
37320      * Destroy this splitbar. 
37321      * @param {Boolean} removeEl True to remove the element
37322      */
37323     destroy : function(removeEl){
37324         if(this.shim){
37325             this.shim.remove();
37326         }
37327         this.dd.unreg();
37328         this.proxy.parentNode.removeChild(this.proxy);
37329         if(removeEl){
37330             this.el.remove();
37331         }
37332     }
37333 });
37334
37335 /**
37336  * @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.
37337  */
37338 Roo.bootstrap.SplitBar.createProxy = function(dir){
37339     var proxy = new Roo.Element(document.createElement("div"));
37340     proxy.unselectable();
37341     var cls = 'roo-splitbar-proxy';
37342     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37343     document.body.appendChild(proxy.dom);
37344     return proxy.dom;
37345 };
37346
37347 /** 
37348  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37349  * Default Adapter. It assumes the splitter and resizing element are not positioned
37350  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37351  */
37352 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37353 };
37354
37355 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37356     // do nothing for now
37357     init : function(s){
37358     
37359     },
37360     /**
37361      * Called before drag operations to get the current size of the resizing element. 
37362      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37363      */
37364      getElementSize : function(s){
37365         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37366             return s.resizingEl.getWidth();
37367         }else{
37368             return s.resizingEl.getHeight();
37369         }
37370     },
37371     
37372     /**
37373      * Called after drag operations to set the size of the resizing element.
37374      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37375      * @param {Number} newSize The new size to set
37376      * @param {Function} onComplete A function to be invoked when resizing is complete
37377      */
37378     setElementSize : function(s, newSize, onComplete){
37379         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37380             if(!s.animate){
37381                 s.resizingEl.setWidth(newSize);
37382                 if(onComplete){
37383                     onComplete(s, newSize);
37384                 }
37385             }else{
37386                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37387             }
37388         }else{
37389             
37390             if(!s.animate){
37391                 s.resizingEl.setHeight(newSize);
37392                 if(onComplete){
37393                     onComplete(s, newSize);
37394                 }
37395             }else{
37396                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37397             }
37398         }
37399     }
37400 };
37401
37402 /** 
37403  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37404  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37405  * Adapter that  moves the splitter element to align with the resized sizing element. 
37406  * Used with an absolute positioned SplitBar.
37407  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37408  * document.body, make sure you assign an id to the body element.
37409  */
37410 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37411     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37412     this.container = Roo.get(container);
37413 };
37414
37415 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37416     init : function(s){
37417         this.basic.init(s);
37418     },
37419     
37420     getElementSize : function(s){
37421         return this.basic.getElementSize(s);
37422     },
37423     
37424     setElementSize : function(s, newSize, onComplete){
37425         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37426     },
37427     
37428     moveSplitter : function(s){
37429         var yes = Roo.bootstrap.SplitBar;
37430         switch(s.placement){
37431             case yes.LEFT:
37432                 s.el.setX(s.resizingEl.getRight());
37433                 break;
37434             case yes.RIGHT:
37435                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37436                 break;
37437             case yes.TOP:
37438                 s.el.setY(s.resizingEl.getBottom());
37439                 break;
37440             case yes.BOTTOM:
37441                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37442                 break;
37443         }
37444     }
37445 };
37446
37447 /**
37448  * Orientation constant - Create a vertical SplitBar
37449  * @static
37450  * @type Number
37451  */
37452 Roo.bootstrap.SplitBar.VERTICAL = 1;
37453
37454 /**
37455  * Orientation constant - Create a horizontal SplitBar
37456  * @static
37457  * @type Number
37458  */
37459 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37460
37461 /**
37462  * Placement constant - The resizing element is to the left of the splitter element
37463  * @static
37464  * @type Number
37465  */
37466 Roo.bootstrap.SplitBar.LEFT = 1;
37467
37468 /**
37469  * Placement constant - The resizing element is to the right of the splitter element
37470  * @static
37471  * @type Number
37472  */
37473 Roo.bootstrap.SplitBar.RIGHT = 2;
37474
37475 /**
37476  * Placement constant - The resizing element is positioned above the splitter element
37477  * @static
37478  * @type Number
37479  */
37480 Roo.bootstrap.SplitBar.TOP = 3;
37481
37482 /**
37483  * Placement constant - The resizing element is positioned under splitter element
37484  * @static
37485  * @type Number
37486  */
37487 Roo.bootstrap.SplitBar.BOTTOM = 4;
37488 Roo.namespace("Roo.bootstrap.layout");/*
37489  * Based on:
37490  * Ext JS Library 1.1.1
37491  * Copyright(c) 2006-2007, Ext JS, LLC.
37492  *
37493  * Originally Released Under LGPL - original licence link has changed is not relivant.
37494  *
37495  * Fork - LGPL
37496  * <script type="text/javascript">
37497  */
37498
37499 /**
37500  * @class Roo.bootstrap.layout.Manager
37501  * @extends Roo.bootstrap.Component
37502  * Base class for layout managers.
37503  */
37504 Roo.bootstrap.layout.Manager = function(config)
37505 {
37506     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37507
37508
37509
37510
37511
37512     /** false to disable window resize monitoring @type Boolean */
37513     this.monitorWindowResize = true;
37514     this.regions = {};
37515     this.addEvents({
37516         /**
37517          * @event layout
37518          * Fires when a layout is performed.
37519          * @param {Roo.LayoutManager} this
37520          */
37521         "layout" : true,
37522         /**
37523          * @event regionresized
37524          * Fires when the user resizes a region.
37525          * @param {Roo.LayoutRegion} region The resized region
37526          * @param {Number} newSize The new size (width for east/west, height for north/south)
37527          */
37528         "regionresized" : true,
37529         /**
37530          * @event regioncollapsed
37531          * Fires when a region is collapsed.
37532          * @param {Roo.LayoutRegion} region The collapsed region
37533          */
37534         "regioncollapsed" : true,
37535         /**
37536          * @event regionexpanded
37537          * Fires when a region is expanded.
37538          * @param {Roo.LayoutRegion} region The expanded region
37539          */
37540         "regionexpanded" : true
37541     });
37542     this.updating = false;
37543
37544     if (config.el) {
37545         this.el = Roo.get(config.el);
37546         this.initEvents();
37547     }
37548
37549 };
37550
37551 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37552
37553
37554     regions : null,
37555
37556     monitorWindowResize : true,
37557
37558
37559     updating : false,
37560
37561
37562     onRender : function(ct, position)
37563     {
37564         if(!this.el){
37565             this.el = Roo.get(ct);
37566             this.initEvents();
37567         }
37568         //this.fireEvent('render',this);
37569     },
37570
37571
37572     initEvents: function()
37573     {
37574
37575
37576         // ie scrollbar fix
37577         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37578             document.body.scroll = "no";
37579         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37580             this.el.position('relative');
37581         }
37582         this.id = this.el.id;
37583         this.el.addClass("roo-layout-container");
37584         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37585         if(this.el.dom != document.body ) {
37586             this.el.on('resize', this.layout,this);
37587             this.el.on('show', this.layout,this);
37588         }
37589
37590     },
37591
37592     /**
37593      * Returns true if this layout is currently being updated
37594      * @return {Boolean}
37595      */
37596     isUpdating : function(){
37597         return this.updating;
37598     },
37599
37600     /**
37601      * Suspend the LayoutManager from doing auto-layouts while
37602      * making multiple add or remove calls
37603      */
37604     beginUpdate : function(){
37605         this.updating = true;
37606     },
37607
37608     /**
37609      * Restore auto-layouts and optionally disable the manager from performing a layout
37610      * @param {Boolean} noLayout true to disable a layout update
37611      */
37612     endUpdate : function(noLayout){
37613         this.updating = false;
37614         if(!noLayout){
37615             this.layout();
37616         }
37617     },
37618
37619     layout: function(){
37620         // abstract...
37621     },
37622
37623     onRegionResized : function(region, newSize){
37624         this.fireEvent("regionresized", region, newSize);
37625         this.layout();
37626     },
37627
37628     onRegionCollapsed : function(region){
37629         this.fireEvent("regioncollapsed", region);
37630     },
37631
37632     onRegionExpanded : function(region){
37633         this.fireEvent("regionexpanded", region);
37634     },
37635
37636     /**
37637      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37638      * performs box-model adjustments.
37639      * @return {Object} The size as an object {width: (the width), height: (the height)}
37640      */
37641     getViewSize : function()
37642     {
37643         var size;
37644         if(this.el.dom != document.body){
37645             size = this.el.getSize();
37646         }else{
37647             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37648         }
37649         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37650         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37651         return size;
37652     },
37653
37654     /**
37655      * Returns the Element this layout is bound to.
37656      * @return {Roo.Element}
37657      */
37658     getEl : function(){
37659         return this.el;
37660     },
37661
37662     /**
37663      * Returns the specified region.
37664      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37665      * @return {Roo.LayoutRegion}
37666      */
37667     getRegion : function(target){
37668         return this.regions[target.toLowerCase()];
37669     },
37670
37671     onWindowResize : function(){
37672         if(this.monitorWindowResize){
37673             this.layout();
37674         }
37675     }
37676 });
37677 /*
37678  * Based on:
37679  * Ext JS Library 1.1.1
37680  * Copyright(c) 2006-2007, Ext JS, LLC.
37681  *
37682  * Originally Released Under LGPL - original licence link has changed is not relivant.
37683  *
37684  * Fork - LGPL
37685  * <script type="text/javascript">
37686  */
37687 /**
37688  * @class Roo.bootstrap.layout.Border
37689  * @extends Roo.bootstrap.layout.Manager
37690  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37691  * please see: examples/bootstrap/nested.html<br><br>
37692  
37693 <b>The container the layout is rendered into can be either the body element or any other element.
37694 If it is not the body element, the container needs to either be an absolute positioned element,
37695 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37696 the container size if it is not the body element.</b>
37697
37698 * @constructor
37699 * Create a new Border
37700 * @param {Object} config Configuration options
37701  */
37702 Roo.bootstrap.layout.Border = function(config){
37703     config = config || {};
37704     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37705     
37706     
37707     
37708     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37709         if(config[region]){
37710             config[region].region = region;
37711             this.addRegion(config[region]);
37712         }
37713     },this);
37714     
37715 };
37716
37717 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37718
37719 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37720     
37721     parent : false, // this might point to a 'nest' or a ???
37722     
37723     /**
37724      * Creates and adds a new region if it doesn't already exist.
37725      * @param {String} target The target region key (north, south, east, west or center).
37726      * @param {Object} config The regions config object
37727      * @return {BorderLayoutRegion} The new region
37728      */
37729     addRegion : function(config)
37730     {
37731         if(!this.regions[config.region]){
37732             var r = this.factory(config);
37733             this.bindRegion(r);
37734         }
37735         return this.regions[config.region];
37736     },
37737
37738     // private (kinda)
37739     bindRegion : function(r){
37740         this.regions[r.config.region] = r;
37741         
37742         r.on("visibilitychange",    this.layout, this);
37743         r.on("paneladded",          this.layout, this);
37744         r.on("panelremoved",        this.layout, this);
37745         r.on("invalidated",         this.layout, this);
37746         r.on("resized",             this.onRegionResized, this);
37747         r.on("collapsed",           this.onRegionCollapsed, this);
37748         r.on("expanded",            this.onRegionExpanded, this);
37749     },
37750
37751     /**
37752      * Performs a layout update.
37753      */
37754     layout : function()
37755     {
37756         if(this.updating) {
37757             return;
37758         }
37759         
37760         // render all the rebions if they have not been done alreayd?
37761         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37762             if(this.regions[region] && !this.regions[region].bodyEl){
37763                 this.regions[region].onRender(this.el)
37764             }
37765         },this);
37766         
37767         var size = this.getViewSize();
37768         var w = size.width;
37769         var h = size.height;
37770         var centerW = w;
37771         var centerH = h;
37772         var centerY = 0;
37773         var centerX = 0;
37774         //var x = 0, y = 0;
37775
37776         var rs = this.regions;
37777         var north = rs["north"];
37778         var south = rs["south"]; 
37779         var west = rs["west"];
37780         var east = rs["east"];
37781         var center = rs["center"];
37782         //if(this.hideOnLayout){ // not supported anymore
37783             //c.el.setStyle("display", "none");
37784         //}
37785         if(north && north.isVisible()){
37786             var b = north.getBox();
37787             var m = north.getMargins();
37788             b.width = w - (m.left+m.right);
37789             b.x = m.left;
37790             b.y = m.top;
37791             centerY = b.height + b.y + m.bottom;
37792             centerH -= centerY;
37793             north.updateBox(this.safeBox(b));
37794         }
37795         if(south && south.isVisible()){
37796             var b = south.getBox();
37797             var m = south.getMargins();
37798             b.width = w - (m.left+m.right);
37799             b.x = m.left;
37800             var totalHeight = (b.height + m.top + m.bottom);
37801             b.y = h - totalHeight + m.top;
37802             centerH -= totalHeight;
37803             south.updateBox(this.safeBox(b));
37804         }
37805         if(west && west.isVisible()){
37806             var b = west.getBox();
37807             var m = west.getMargins();
37808             b.height = centerH - (m.top+m.bottom);
37809             b.x = m.left;
37810             b.y = centerY + m.top;
37811             var totalWidth = (b.width + m.left + m.right);
37812             centerX += totalWidth;
37813             centerW -= totalWidth;
37814             west.updateBox(this.safeBox(b));
37815         }
37816         if(east && east.isVisible()){
37817             var b = east.getBox();
37818             var m = east.getMargins();
37819             b.height = centerH - (m.top+m.bottom);
37820             var totalWidth = (b.width + m.left + m.right);
37821             b.x = w - totalWidth + m.left;
37822             b.y = centerY + m.top;
37823             centerW -= totalWidth;
37824             east.updateBox(this.safeBox(b));
37825         }
37826         if(center){
37827             var m = center.getMargins();
37828             var centerBox = {
37829                 x: centerX + m.left,
37830                 y: centerY + m.top,
37831                 width: centerW - (m.left+m.right),
37832                 height: centerH - (m.top+m.bottom)
37833             };
37834             //if(this.hideOnLayout){
37835                 //center.el.setStyle("display", "block");
37836             //}
37837             center.updateBox(this.safeBox(centerBox));
37838         }
37839         this.el.repaint();
37840         this.fireEvent("layout", this);
37841     },
37842
37843     // private
37844     safeBox : function(box){
37845         box.width = Math.max(0, box.width);
37846         box.height = Math.max(0, box.height);
37847         return box;
37848     },
37849
37850     /**
37851      * Adds a ContentPanel (or subclass) to this layout.
37852      * @param {String} target The target region key (north, south, east, west or center).
37853      * @param {Roo.ContentPanel} panel The panel to add
37854      * @return {Roo.ContentPanel} The added panel
37855      */
37856     add : function(target, panel){
37857          
37858         target = target.toLowerCase();
37859         return this.regions[target].add(panel);
37860     },
37861
37862     /**
37863      * Remove a ContentPanel (or subclass) to this layout.
37864      * @param {String} target The target region key (north, south, east, west or center).
37865      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37866      * @return {Roo.ContentPanel} The removed panel
37867      */
37868     remove : function(target, panel){
37869         target = target.toLowerCase();
37870         return this.regions[target].remove(panel);
37871     },
37872
37873     /**
37874      * Searches all regions for a panel with the specified id
37875      * @param {String} panelId
37876      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37877      */
37878     findPanel : function(panelId){
37879         var rs = this.regions;
37880         for(var target in rs){
37881             if(typeof rs[target] != "function"){
37882                 var p = rs[target].getPanel(panelId);
37883                 if(p){
37884                     return p;
37885                 }
37886             }
37887         }
37888         return null;
37889     },
37890
37891     /**
37892      * Searches all regions for a panel with the specified id and activates (shows) it.
37893      * @param {String/ContentPanel} panelId The panels id or the panel itself
37894      * @return {Roo.ContentPanel} The shown panel or null
37895      */
37896     showPanel : function(panelId) {
37897       var rs = this.regions;
37898       for(var target in rs){
37899          var r = rs[target];
37900          if(typeof r != "function"){
37901             if(r.hasPanel(panelId)){
37902                return r.showPanel(panelId);
37903             }
37904          }
37905       }
37906       return null;
37907    },
37908
37909    /**
37910      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37911      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37912      */
37913    /*
37914     restoreState : function(provider){
37915         if(!provider){
37916             provider = Roo.state.Manager;
37917         }
37918         var sm = new Roo.LayoutStateManager();
37919         sm.init(this, provider);
37920     },
37921 */
37922  
37923  
37924     /**
37925      * Adds a xtype elements to the layout.
37926      * <pre><code>
37927
37928 layout.addxtype({
37929        xtype : 'ContentPanel',
37930        region: 'west',
37931        items: [ .... ]
37932    }
37933 );
37934
37935 layout.addxtype({
37936         xtype : 'NestedLayoutPanel',
37937         region: 'west',
37938         layout: {
37939            center: { },
37940            west: { }   
37941         },
37942         items : [ ... list of content panels or nested layout panels.. ]
37943    }
37944 );
37945 </code></pre>
37946      * @param {Object} cfg Xtype definition of item to add.
37947      */
37948     addxtype : function(cfg)
37949     {
37950         // basically accepts a pannel...
37951         // can accept a layout region..!?!?
37952         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37953         
37954         
37955         // theory?  children can only be panels??
37956         
37957         //if (!cfg.xtype.match(/Panel$/)) {
37958         //    return false;
37959         //}
37960         var ret = false;
37961         
37962         if (typeof(cfg.region) == 'undefined') {
37963             Roo.log("Failed to add Panel, region was not set");
37964             Roo.log(cfg);
37965             return false;
37966         }
37967         var region = cfg.region;
37968         delete cfg.region;
37969         
37970           
37971         var xitems = [];
37972         if (cfg.items) {
37973             xitems = cfg.items;
37974             delete cfg.items;
37975         }
37976         var nb = false;
37977         
37978         if ( region == 'center') {
37979             Roo.log("Center: " + cfg.title);
37980         }
37981         
37982         
37983         switch(cfg.xtype) 
37984         {
37985             case 'Content':  // ContentPanel (el, cfg)
37986             case 'Scroll':  // ContentPanel (el, cfg)
37987             case 'View': 
37988                 cfg.autoCreate = cfg.autoCreate || true;
37989                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37990                 //} else {
37991                 //    var el = this.el.createChild();
37992                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37993                 //}
37994                 
37995                 this.add(region, ret);
37996                 break;
37997             
37998             /*
37999             case 'TreePanel': // our new panel!
38000                 cfg.el = this.el.createChild();
38001                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38002                 this.add(region, ret);
38003                 break;
38004             */
38005             
38006             case 'Nest': 
38007                 // create a new Layout (which is  a Border Layout...
38008                 
38009                 var clayout = cfg.layout;
38010                 clayout.el  = this.el.createChild();
38011                 clayout.items   = clayout.items  || [];
38012                 
38013                 delete cfg.layout;
38014                 
38015                 // replace this exitems with the clayout ones..
38016                 xitems = clayout.items;
38017                  
38018                 // force background off if it's in center...
38019                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38020                     cfg.background = false;
38021                 }
38022                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38023                 
38024                 
38025                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38026                 //console.log('adding nested layout panel '  + cfg.toSource());
38027                 this.add(region, ret);
38028                 nb = {}; /// find first...
38029                 break;
38030             
38031             case 'Grid':
38032                 
38033                 // needs grid and region
38034                 
38035                 //var el = this.getRegion(region).el.createChild();
38036                 /*
38037                  *var el = this.el.createChild();
38038                 // create the grid first...
38039                 cfg.grid.container = el;
38040                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38041                 */
38042                 
38043                 if (region == 'center' && this.active ) {
38044                     cfg.background = false;
38045                 }
38046                 
38047                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38048                 
38049                 this.add(region, ret);
38050                 /*
38051                 if (cfg.background) {
38052                     // render grid on panel activation (if panel background)
38053                     ret.on('activate', function(gp) {
38054                         if (!gp.grid.rendered) {
38055                     //        gp.grid.render(el);
38056                         }
38057                     });
38058                 } else {
38059                   //  cfg.grid.render(el);
38060                 }
38061                 */
38062                 break;
38063            
38064            
38065             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38066                 // it was the old xcomponent building that caused this before.
38067                 // espeically if border is the top element in the tree.
38068                 ret = this;
38069                 break; 
38070                 
38071                     
38072                 
38073                 
38074                 
38075             default:
38076                 /*
38077                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38078                     
38079                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38080                     this.add(region, ret);
38081                 } else {
38082                 */
38083                     Roo.log(cfg);
38084                     throw "Can not add '" + cfg.xtype + "' to Border";
38085                     return null;
38086              
38087                                 
38088              
38089         }
38090         this.beginUpdate();
38091         // add children..
38092         var region = '';
38093         var abn = {};
38094         Roo.each(xitems, function(i)  {
38095             region = nb && i.region ? i.region : false;
38096             
38097             var add = ret.addxtype(i);
38098            
38099             if (region) {
38100                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38101                 if (!i.background) {
38102                     abn[region] = nb[region] ;
38103                 }
38104             }
38105             
38106         });
38107         this.endUpdate();
38108
38109         // make the last non-background panel active..
38110         //if (nb) { Roo.log(abn); }
38111         if (nb) {
38112             
38113             for(var r in abn) {
38114                 region = this.getRegion(r);
38115                 if (region) {
38116                     // tried using nb[r], but it does not work..
38117                      
38118                     region.showPanel(abn[r]);
38119                    
38120                 }
38121             }
38122         }
38123         return ret;
38124         
38125     },
38126     
38127     
38128 // private
38129     factory : function(cfg)
38130     {
38131         
38132         var validRegions = Roo.bootstrap.layout.Border.regions;
38133
38134         var target = cfg.region;
38135         cfg.mgr = this;
38136         
38137         var r = Roo.bootstrap.layout;
38138         Roo.log(target);
38139         switch(target){
38140             case "north":
38141                 return new r.North(cfg);
38142             case "south":
38143                 return new r.South(cfg);
38144             case "east":
38145                 return new r.East(cfg);
38146             case "west":
38147                 return new r.West(cfg);
38148             case "center":
38149                 return new r.Center(cfg);
38150         }
38151         throw 'Layout region "'+target+'" not supported.';
38152     }
38153     
38154     
38155 });
38156  /*
38157  * Based on:
38158  * Ext JS Library 1.1.1
38159  * Copyright(c) 2006-2007, Ext JS, LLC.
38160  *
38161  * Originally Released Under LGPL - original licence link has changed is not relivant.
38162  *
38163  * Fork - LGPL
38164  * <script type="text/javascript">
38165  */
38166  
38167 /**
38168  * @class Roo.bootstrap.layout.Basic
38169  * @extends Roo.util.Observable
38170  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38171  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38172  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38173  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38174  * @cfg {string}   region  the region that it inhabits..
38175  * @cfg {bool}   skipConfig skip config?
38176  * 
38177
38178  */
38179 Roo.bootstrap.layout.Basic = function(config){
38180     
38181     this.mgr = config.mgr;
38182     
38183     this.position = config.region;
38184     
38185     var skipConfig = config.skipConfig;
38186     
38187     this.events = {
38188         /**
38189          * @scope Roo.BasicLayoutRegion
38190          */
38191         
38192         /**
38193          * @event beforeremove
38194          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38195          * @param {Roo.LayoutRegion} this
38196          * @param {Roo.ContentPanel} panel The panel
38197          * @param {Object} e The cancel event object
38198          */
38199         "beforeremove" : true,
38200         /**
38201          * @event invalidated
38202          * Fires when the layout for this region is changed.
38203          * @param {Roo.LayoutRegion} this
38204          */
38205         "invalidated" : true,
38206         /**
38207          * @event visibilitychange
38208          * Fires when this region is shown or hidden 
38209          * @param {Roo.LayoutRegion} this
38210          * @param {Boolean} visibility true or false
38211          */
38212         "visibilitychange" : true,
38213         /**
38214          * @event paneladded
38215          * Fires when a panel is added. 
38216          * @param {Roo.LayoutRegion} this
38217          * @param {Roo.ContentPanel} panel The panel
38218          */
38219         "paneladded" : true,
38220         /**
38221          * @event panelremoved
38222          * Fires when a panel is removed. 
38223          * @param {Roo.LayoutRegion} this
38224          * @param {Roo.ContentPanel} panel The panel
38225          */
38226         "panelremoved" : true,
38227         /**
38228          * @event beforecollapse
38229          * Fires when this region before collapse.
38230          * @param {Roo.LayoutRegion} this
38231          */
38232         "beforecollapse" : true,
38233         /**
38234          * @event collapsed
38235          * Fires when this region is collapsed.
38236          * @param {Roo.LayoutRegion} this
38237          */
38238         "collapsed" : true,
38239         /**
38240          * @event expanded
38241          * Fires when this region is expanded.
38242          * @param {Roo.LayoutRegion} this
38243          */
38244         "expanded" : true,
38245         /**
38246          * @event slideshow
38247          * Fires when this region is slid into view.
38248          * @param {Roo.LayoutRegion} this
38249          */
38250         "slideshow" : true,
38251         /**
38252          * @event slidehide
38253          * Fires when this region slides out of view. 
38254          * @param {Roo.LayoutRegion} this
38255          */
38256         "slidehide" : true,
38257         /**
38258          * @event panelactivated
38259          * Fires when a panel is activated. 
38260          * @param {Roo.LayoutRegion} this
38261          * @param {Roo.ContentPanel} panel The activated panel
38262          */
38263         "panelactivated" : true,
38264         /**
38265          * @event resized
38266          * Fires when the user resizes this region. 
38267          * @param {Roo.LayoutRegion} this
38268          * @param {Number} newSize The new size (width for east/west, height for north/south)
38269          */
38270         "resized" : true
38271     };
38272     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38273     this.panels = new Roo.util.MixedCollection();
38274     this.panels.getKey = this.getPanelId.createDelegate(this);
38275     this.box = null;
38276     this.activePanel = null;
38277     // ensure listeners are added...
38278     
38279     if (config.listeners || config.events) {
38280         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38281             listeners : config.listeners || {},
38282             events : config.events || {}
38283         });
38284     }
38285     
38286     if(skipConfig !== true){
38287         this.applyConfig(config);
38288     }
38289 };
38290
38291 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38292 {
38293     getPanelId : function(p){
38294         return p.getId();
38295     },
38296     
38297     applyConfig : function(config){
38298         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38299         this.config = config;
38300         
38301     },
38302     
38303     /**
38304      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38305      * the width, for horizontal (north, south) the height.
38306      * @param {Number} newSize The new width or height
38307      */
38308     resizeTo : function(newSize){
38309         var el = this.el ? this.el :
38310                  (this.activePanel ? this.activePanel.getEl() : null);
38311         if(el){
38312             switch(this.position){
38313                 case "east":
38314                 case "west":
38315                     el.setWidth(newSize);
38316                     this.fireEvent("resized", this, newSize);
38317                 break;
38318                 case "north":
38319                 case "south":
38320                     el.setHeight(newSize);
38321                     this.fireEvent("resized", this, newSize);
38322                 break;                
38323             }
38324         }
38325     },
38326     
38327     getBox : function(){
38328         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38329     },
38330     
38331     getMargins : function(){
38332         return this.margins;
38333     },
38334     
38335     updateBox : function(box){
38336         this.box = box;
38337         var el = this.activePanel.getEl();
38338         el.dom.style.left = box.x + "px";
38339         el.dom.style.top = box.y + "px";
38340         this.activePanel.setSize(box.width, box.height);
38341     },
38342     
38343     /**
38344      * Returns the container element for this region.
38345      * @return {Roo.Element}
38346      */
38347     getEl : function(){
38348         return this.activePanel;
38349     },
38350     
38351     /**
38352      * Returns true if this region is currently visible.
38353      * @return {Boolean}
38354      */
38355     isVisible : function(){
38356         return this.activePanel ? true : false;
38357     },
38358     
38359     setActivePanel : function(panel){
38360         panel = this.getPanel(panel);
38361         if(this.activePanel && this.activePanel != panel){
38362             this.activePanel.setActiveState(false);
38363             this.activePanel.getEl().setLeftTop(-10000,-10000);
38364         }
38365         this.activePanel = panel;
38366         panel.setActiveState(true);
38367         if(this.box){
38368             panel.setSize(this.box.width, this.box.height);
38369         }
38370         this.fireEvent("panelactivated", this, panel);
38371         this.fireEvent("invalidated");
38372     },
38373     
38374     /**
38375      * Show the specified panel.
38376      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38377      * @return {Roo.ContentPanel} The shown panel or null
38378      */
38379     showPanel : function(panel){
38380         panel = this.getPanel(panel);
38381         if(panel){
38382             this.setActivePanel(panel);
38383         }
38384         return panel;
38385     },
38386     
38387     /**
38388      * Get the active panel for this region.
38389      * @return {Roo.ContentPanel} The active panel or null
38390      */
38391     getActivePanel : function(){
38392         return this.activePanel;
38393     },
38394     
38395     /**
38396      * Add the passed ContentPanel(s)
38397      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38398      * @return {Roo.ContentPanel} The panel added (if only one was added)
38399      */
38400     add : function(panel){
38401         if(arguments.length > 1){
38402             for(var i = 0, len = arguments.length; i < len; i++) {
38403                 this.add(arguments[i]);
38404             }
38405             return null;
38406         }
38407         if(this.hasPanel(panel)){
38408             this.showPanel(panel);
38409             return panel;
38410         }
38411         var el = panel.getEl();
38412         if(el.dom.parentNode != this.mgr.el.dom){
38413             this.mgr.el.dom.appendChild(el.dom);
38414         }
38415         if(panel.setRegion){
38416             panel.setRegion(this);
38417         }
38418         this.panels.add(panel);
38419         el.setStyle("position", "absolute");
38420         if(!panel.background){
38421             this.setActivePanel(panel);
38422             if(this.config.initialSize && this.panels.getCount()==1){
38423                 this.resizeTo(this.config.initialSize);
38424             }
38425         }
38426         this.fireEvent("paneladded", this, panel);
38427         return panel;
38428     },
38429     
38430     /**
38431      * Returns true if the panel is in this region.
38432      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38433      * @return {Boolean}
38434      */
38435     hasPanel : function(panel){
38436         if(typeof panel == "object"){ // must be panel obj
38437             panel = panel.getId();
38438         }
38439         return this.getPanel(panel) ? true : false;
38440     },
38441     
38442     /**
38443      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38444      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38445      * @param {Boolean} preservePanel Overrides the config preservePanel option
38446      * @return {Roo.ContentPanel} The panel that was removed
38447      */
38448     remove : function(panel, preservePanel){
38449         panel = this.getPanel(panel);
38450         if(!panel){
38451             return null;
38452         }
38453         var e = {};
38454         this.fireEvent("beforeremove", this, panel, e);
38455         if(e.cancel === true){
38456             return null;
38457         }
38458         var panelId = panel.getId();
38459         this.panels.removeKey(panelId);
38460         return panel;
38461     },
38462     
38463     /**
38464      * Returns the panel specified or null if it's not in this region.
38465      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38466      * @return {Roo.ContentPanel}
38467      */
38468     getPanel : function(id){
38469         if(typeof id == "object"){ // must be panel obj
38470             return id;
38471         }
38472         return this.panels.get(id);
38473     },
38474     
38475     /**
38476      * Returns this regions position (north/south/east/west/center).
38477      * @return {String} 
38478      */
38479     getPosition: function(){
38480         return this.position;    
38481     }
38482 });/*
38483  * Based on:
38484  * Ext JS Library 1.1.1
38485  * Copyright(c) 2006-2007, Ext JS, LLC.
38486  *
38487  * Originally Released Under LGPL - original licence link has changed is not relivant.
38488  *
38489  * Fork - LGPL
38490  * <script type="text/javascript">
38491  */
38492  
38493 /**
38494  * @class Roo.bootstrap.layout.Region
38495  * @extends Roo.bootstrap.layout.Basic
38496  * This class represents a region in a layout manager.
38497  
38498  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38499  * @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})
38500  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38501  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38502  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38503  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38504  * @cfg {String}    title           The title for the region (overrides panel titles)
38505  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38506  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38507  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38508  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38509  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38510  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38511  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38512  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38513  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38514  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38515
38516  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38517  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38518  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38519  * @cfg {Number}    width           For East/West panels
38520  * @cfg {Number}    height          For North/South panels
38521  * @cfg {Boolean}   split           To show the splitter
38522  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38523  * 
38524  * @cfg {string}   cls             Extra CSS classes to add to region
38525  * 
38526  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38527  * @cfg {string}   region  the region that it inhabits..
38528  *
38529
38530  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38531  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38532
38533  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38534  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38535  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38536  */
38537 Roo.bootstrap.layout.Region = function(config)
38538 {
38539     this.applyConfig(config);
38540
38541     var mgr = config.mgr;
38542     var pos = config.region;
38543     config.skipConfig = true;
38544     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38545     
38546     if (mgr.el) {
38547         this.onRender(mgr.el);   
38548     }
38549      
38550     this.visible = true;
38551     this.collapsed = false;
38552     this.unrendered_panels = [];
38553 };
38554
38555 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38556
38557     position: '', // set by wrapper (eg. north/south etc..)
38558     unrendered_panels : null,  // unrendered panels.
38559     
38560     tabPosition : false,
38561     
38562     mgr: false, // points to 'Border'
38563     
38564     
38565     createBody : function(){
38566         /** This region's body element 
38567         * @type Roo.Element */
38568         this.bodyEl = this.el.createChild({
38569                 tag: "div",
38570                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38571         });
38572     },
38573
38574     onRender: function(ctr, pos)
38575     {
38576         var dh = Roo.DomHelper;
38577         /** This region's container element 
38578         * @type Roo.Element */
38579         this.el = dh.append(ctr.dom, {
38580                 tag: "div",
38581                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38582             }, true);
38583         /** This region's title element 
38584         * @type Roo.Element */
38585     
38586         this.titleEl = dh.append(this.el.dom,  {
38587                 tag: "div",
38588                 unselectable: "on",
38589                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38590                 children:[
38591                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38592                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38593                 ]
38594             }, true);
38595         
38596         this.titleEl.enableDisplayMode();
38597         /** This region's title text element 
38598         * @type HTMLElement */
38599         this.titleTextEl = this.titleEl.dom.firstChild;
38600         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38601         /*
38602         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38603         this.closeBtn.enableDisplayMode();
38604         this.closeBtn.on("click", this.closeClicked, this);
38605         this.closeBtn.hide();
38606     */
38607         this.createBody(this.config);
38608         if(this.config.hideWhenEmpty){
38609             this.hide();
38610             this.on("paneladded", this.validateVisibility, this);
38611             this.on("panelremoved", this.validateVisibility, this);
38612         }
38613         if(this.autoScroll){
38614             this.bodyEl.setStyle("overflow", "auto");
38615         }else{
38616             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38617         }
38618         //if(c.titlebar !== false){
38619             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38620                 this.titleEl.hide();
38621             }else{
38622                 this.titleEl.show();
38623                 if(this.config.title){
38624                     this.titleTextEl.innerHTML = this.config.title;
38625                 }
38626             }
38627         //}
38628         if(this.config.collapsed){
38629             this.collapse(true);
38630         }
38631         if(this.config.hidden){
38632             this.hide();
38633         }
38634         
38635         if (this.unrendered_panels && this.unrendered_panels.length) {
38636             for (var i =0;i< this.unrendered_panels.length; i++) {
38637                 this.add(this.unrendered_panels[i]);
38638             }
38639             this.unrendered_panels = null;
38640             
38641         }
38642         
38643     },
38644     
38645     applyConfig : function(c)
38646     {
38647         /*
38648          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38649             var dh = Roo.DomHelper;
38650             if(c.titlebar !== false){
38651                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38652                 this.collapseBtn.on("click", this.collapse, this);
38653                 this.collapseBtn.enableDisplayMode();
38654                 /*
38655                 if(c.showPin === true || this.showPin){
38656                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38657                     this.stickBtn.enableDisplayMode();
38658                     this.stickBtn.on("click", this.expand, this);
38659                     this.stickBtn.hide();
38660                 }
38661                 
38662             }
38663             */
38664             /** This region's collapsed element
38665             * @type Roo.Element */
38666             /*
38667              *
38668             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38669                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38670             ]}, true);
38671             
38672             if(c.floatable !== false){
38673                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38674                this.collapsedEl.on("click", this.collapseClick, this);
38675             }
38676
38677             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38678                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38679                    id: "message", unselectable: "on", style:{"float":"left"}});
38680                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38681              }
38682             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38683             this.expandBtn.on("click", this.expand, this);
38684             
38685         }
38686         
38687         if(this.collapseBtn){
38688             this.collapseBtn.setVisible(c.collapsible == true);
38689         }
38690         
38691         this.cmargins = c.cmargins || this.cmargins ||
38692                          (this.position == "west" || this.position == "east" ?
38693                              {top: 0, left: 2, right:2, bottom: 0} :
38694                              {top: 2, left: 0, right:0, bottom: 2});
38695         */
38696         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38697         
38698         
38699         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38700         
38701         this.autoScroll = c.autoScroll || false;
38702         
38703         
38704        
38705         
38706         this.duration = c.duration || .30;
38707         this.slideDuration = c.slideDuration || .45;
38708         this.config = c;
38709        
38710     },
38711     /**
38712      * Returns true if this region is currently visible.
38713      * @return {Boolean}
38714      */
38715     isVisible : function(){
38716         return this.visible;
38717     },
38718
38719     /**
38720      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38721      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38722      */
38723     //setCollapsedTitle : function(title){
38724     //    title = title || "&#160;";
38725      //   if(this.collapsedTitleTextEl){
38726       //      this.collapsedTitleTextEl.innerHTML = title;
38727        // }
38728     //},
38729
38730     getBox : function(){
38731         var b;
38732       //  if(!this.collapsed){
38733             b = this.el.getBox(false, true);
38734        // }else{
38735           //  b = this.collapsedEl.getBox(false, true);
38736         //}
38737         return b;
38738     },
38739
38740     getMargins : function(){
38741         return this.margins;
38742         //return this.collapsed ? this.cmargins : this.margins;
38743     },
38744 /*
38745     highlight : function(){
38746         this.el.addClass("x-layout-panel-dragover");
38747     },
38748
38749     unhighlight : function(){
38750         this.el.removeClass("x-layout-panel-dragover");
38751     },
38752 */
38753     updateBox : function(box)
38754     {
38755         if (!this.bodyEl) {
38756             return; // not rendered yet..
38757         }
38758         
38759         this.box = box;
38760         if(!this.collapsed){
38761             this.el.dom.style.left = box.x + "px";
38762             this.el.dom.style.top = box.y + "px";
38763             this.updateBody(box.width, box.height);
38764         }else{
38765             this.collapsedEl.dom.style.left = box.x + "px";
38766             this.collapsedEl.dom.style.top = box.y + "px";
38767             this.collapsedEl.setSize(box.width, box.height);
38768         }
38769         if(this.tabs){
38770             this.tabs.autoSizeTabs();
38771         }
38772     },
38773
38774     updateBody : function(w, h)
38775     {
38776         if(w !== null){
38777             this.el.setWidth(w);
38778             w -= this.el.getBorderWidth("rl");
38779             if(this.config.adjustments){
38780                 w += this.config.adjustments[0];
38781             }
38782         }
38783         if(h !== null && h > 0){
38784             this.el.setHeight(h);
38785             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38786             h -= this.el.getBorderWidth("tb");
38787             if(this.config.adjustments){
38788                 h += this.config.adjustments[1];
38789             }
38790             this.bodyEl.setHeight(h);
38791             if(this.tabs){
38792                 h = this.tabs.syncHeight(h);
38793             }
38794         }
38795         if(this.panelSize){
38796             w = w !== null ? w : this.panelSize.width;
38797             h = h !== null ? h : this.panelSize.height;
38798         }
38799         if(this.activePanel){
38800             var el = this.activePanel.getEl();
38801             w = w !== null ? w : el.getWidth();
38802             h = h !== null ? h : el.getHeight();
38803             this.panelSize = {width: w, height: h};
38804             this.activePanel.setSize(w, h);
38805         }
38806         if(Roo.isIE && this.tabs){
38807             this.tabs.el.repaint();
38808         }
38809     },
38810
38811     /**
38812      * Returns the container element for this region.
38813      * @return {Roo.Element}
38814      */
38815     getEl : function(){
38816         return this.el;
38817     },
38818
38819     /**
38820      * Hides this region.
38821      */
38822     hide : function(){
38823         //if(!this.collapsed){
38824             this.el.dom.style.left = "-2000px";
38825             this.el.hide();
38826         //}else{
38827          //   this.collapsedEl.dom.style.left = "-2000px";
38828          //   this.collapsedEl.hide();
38829        // }
38830         this.visible = false;
38831         this.fireEvent("visibilitychange", this, false);
38832     },
38833
38834     /**
38835      * Shows this region if it was previously hidden.
38836      */
38837     show : function(){
38838         //if(!this.collapsed){
38839             this.el.show();
38840         //}else{
38841         //    this.collapsedEl.show();
38842        // }
38843         this.visible = true;
38844         this.fireEvent("visibilitychange", this, true);
38845     },
38846 /*
38847     closeClicked : function(){
38848         if(this.activePanel){
38849             this.remove(this.activePanel);
38850         }
38851     },
38852
38853     collapseClick : function(e){
38854         if(this.isSlid){
38855            e.stopPropagation();
38856            this.slideIn();
38857         }else{
38858            e.stopPropagation();
38859            this.slideOut();
38860         }
38861     },
38862 */
38863     /**
38864      * Collapses this region.
38865      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38866      */
38867     /*
38868     collapse : function(skipAnim, skipCheck = false){
38869         if(this.collapsed) {
38870             return;
38871         }
38872         
38873         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38874             
38875             this.collapsed = true;
38876             if(this.split){
38877                 this.split.el.hide();
38878             }
38879             if(this.config.animate && skipAnim !== true){
38880                 this.fireEvent("invalidated", this);
38881                 this.animateCollapse();
38882             }else{
38883                 this.el.setLocation(-20000,-20000);
38884                 this.el.hide();
38885                 this.collapsedEl.show();
38886                 this.fireEvent("collapsed", this);
38887                 this.fireEvent("invalidated", this);
38888             }
38889         }
38890         
38891     },
38892 */
38893     animateCollapse : function(){
38894         // overridden
38895     },
38896
38897     /**
38898      * Expands this region if it was previously collapsed.
38899      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38900      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38901      */
38902     /*
38903     expand : function(e, skipAnim){
38904         if(e) {
38905             e.stopPropagation();
38906         }
38907         if(!this.collapsed || this.el.hasActiveFx()) {
38908             return;
38909         }
38910         if(this.isSlid){
38911             this.afterSlideIn();
38912             skipAnim = true;
38913         }
38914         this.collapsed = false;
38915         if(this.config.animate && skipAnim !== true){
38916             this.animateExpand();
38917         }else{
38918             this.el.show();
38919             if(this.split){
38920                 this.split.el.show();
38921             }
38922             this.collapsedEl.setLocation(-2000,-2000);
38923             this.collapsedEl.hide();
38924             this.fireEvent("invalidated", this);
38925             this.fireEvent("expanded", this);
38926         }
38927     },
38928 */
38929     animateExpand : function(){
38930         // overridden
38931     },
38932
38933     initTabs : function()
38934     {
38935         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38936         
38937         var ts = new Roo.bootstrap.panel.Tabs({
38938             el: this.bodyEl.dom,
38939             region : this,
38940             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38941             disableTooltips: this.config.disableTabTips,
38942             toolbar : this.config.toolbar
38943         });
38944         
38945         if(this.config.hideTabs){
38946             ts.stripWrap.setDisplayed(false);
38947         }
38948         this.tabs = ts;
38949         ts.resizeTabs = this.config.resizeTabs === true;
38950         ts.minTabWidth = this.config.minTabWidth || 40;
38951         ts.maxTabWidth = this.config.maxTabWidth || 250;
38952         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38953         ts.monitorResize = false;
38954         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38955         ts.bodyEl.addClass('roo-layout-tabs-body');
38956         this.panels.each(this.initPanelAsTab, this);
38957     },
38958
38959     initPanelAsTab : function(panel){
38960         var ti = this.tabs.addTab(
38961             panel.getEl().id,
38962             panel.getTitle(),
38963             null,
38964             this.config.closeOnTab && panel.isClosable(),
38965             panel.tpl
38966         );
38967         if(panel.tabTip !== undefined){
38968             ti.setTooltip(panel.tabTip);
38969         }
38970         ti.on("activate", function(){
38971               this.setActivePanel(panel);
38972         }, this);
38973         
38974         if(this.config.closeOnTab){
38975             ti.on("beforeclose", function(t, e){
38976                 e.cancel = true;
38977                 this.remove(panel);
38978             }, this);
38979         }
38980         
38981         panel.tabItem = ti;
38982         
38983         return ti;
38984     },
38985
38986     updatePanelTitle : function(panel, title)
38987     {
38988         if(this.activePanel == panel){
38989             this.updateTitle(title);
38990         }
38991         if(this.tabs){
38992             var ti = this.tabs.getTab(panel.getEl().id);
38993             ti.setText(title);
38994             if(panel.tabTip !== undefined){
38995                 ti.setTooltip(panel.tabTip);
38996             }
38997         }
38998     },
38999
39000     updateTitle : function(title){
39001         if(this.titleTextEl && !this.config.title){
39002             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39003         }
39004     },
39005
39006     setActivePanel : function(panel)
39007     {
39008         panel = this.getPanel(panel);
39009         if(this.activePanel && this.activePanel != panel){
39010             if(this.activePanel.setActiveState(false) === false){
39011                 return;
39012             }
39013         }
39014         this.activePanel = panel;
39015         panel.setActiveState(true);
39016         if(this.panelSize){
39017             panel.setSize(this.panelSize.width, this.panelSize.height);
39018         }
39019         if(this.closeBtn){
39020             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39021         }
39022         this.updateTitle(panel.getTitle());
39023         if(this.tabs){
39024             this.fireEvent("invalidated", this);
39025         }
39026         this.fireEvent("panelactivated", this, panel);
39027     },
39028
39029     /**
39030      * Shows the specified panel.
39031      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39032      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39033      */
39034     showPanel : function(panel)
39035     {
39036         panel = this.getPanel(panel);
39037         if(panel){
39038             if(this.tabs){
39039                 var tab = this.tabs.getTab(panel.getEl().id);
39040                 if(tab.isHidden()){
39041                     this.tabs.unhideTab(tab.id);
39042                 }
39043                 tab.activate();
39044             }else{
39045                 this.setActivePanel(panel);
39046             }
39047         }
39048         return panel;
39049     },
39050
39051     /**
39052      * Get the active panel for this region.
39053      * @return {Roo.ContentPanel} The active panel or null
39054      */
39055     getActivePanel : function(){
39056         return this.activePanel;
39057     },
39058
39059     validateVisibility : function(){
39060         if(this.panels.getCount() < 1){
39061             this.updateTitle("&#160;");
39062             this.closeBtn.hide();
39063             this.hide();
39064         }else{
39065             if(!this.isVisible()){
39066                 this.show();
39067             }
39068         }
39069     },
39070
39071     /**
39072      * Adds the passed ContentPanel(s) to this region.
39073      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39074      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39075      */
39076     add : function(panel)
39077     {
39078         if(arguments.length > 1){
39079             for(var i = 0, len = arguments.length; i < len; i++) {
39080                 this.add(arguments[i]);
39081             }
39082             return null;
39083         }
39084         
39085         // if we have not been rendered yet, then we can not really do much of this..
39086         if (!this.bodyEl) {
39087             this.unrendered_panels.push(panel);
39088             return panel;
39089         }
39090         
39091         
39092         
39093         
39094         if(this.hasPanel(panel)){
39095             this.showPanel(panel);
39096             return panel;
39097         }
39098         panel.setRegion(this);
39099         this.panels.add(panel);
39100        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39101             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39102             // and hide them... ???
39103             this.bodyEl.dom.appendChild(panel.getEl().dom);
39104             if(panel.background !== true){
39105                 this.setActivePanel(panel);
39106             }
39107             this.fireEvent("paneladded", this, panel);
39108             return panel;
39109         }
39110         */
39111         if(!this.tabs){
39112             this.initTabs();
39113         }else{
39114             this.initPanelAsTab(panel);
39115         }
39116         
39117         
39118         if(panel.background !== true){
39119             this.tabs.activate(panel.getEl().id);
39120         }
39121         this.fireEvent("paneladded", this, panel);
39122         return panel;
39123     },
39124
39125     /**
39126      * Hides the tab for the specified panel.
39127      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39128      */
39129     hidePanel : function(panel){
39130         if(this.tabs && (panel = this.getPanel(panel))){
39131             this.tabs.hideTab(panel.getEl().id);
39132         }
39133     },
39134
39135     /**
39136      * Unhides the tab for a previously hidden panel.
39137      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39138      */
39139     unhidePanel : function(panel){
39140         if(this.tabs && (panel = this.getPanel(panel))){
39141             this.tabs.unhideTab(panel.getEl().id);
39142         }
39143     },
39144
39145     clearPanels : function(){
39146         while(this.panels.getCount() > 0){
39147              this.remove(this.panels.first());
39148         }
39149     },
39150
39151     /**
39152      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39153      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39154      * @param {Boolean} preservePanel Overrides the config preservePanel option
39155      * @return {Roo.ContentPanel} The panel that was removed
39156      */
39157     remove : function(panel, preservePanel)
39158     {
39159         panel = this.getPanel(panel);
39160         if(!panel){
39161             return null;
39162         }
39163         var e = {};
39164         this.fireEvent("beforeremove", this, panel, e);
39165         if(e.cancel === true){
39166             return null;
39167         }
39168         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39169         var panelId = panel.getId();
39170         this.panels.removeKey(panelId);
39171         if(preservePanel){
39172             document.body.appendChild(panel.getEl().dom);
39173         }
39174         if(this.tabs){
39175             this.tabs.removeTab(panel.getEl().id);
39176         }else if (!preservePanel){
39177             this.bodyEl.dom.removeChild(panel.getEl().dom);
39178         }
39179         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39180             var p = this.panels.first();
39181             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39182             tempEl.appendChild(p.getEl().dom);
39183             this.bodyEl.update("");
39184             this.bodyEl.dom.appendChild(p.getEl().dom);
39185             tempEl = null;
39186             this.updateTitle(p.getTitle());
39187             this.tabs = null;
39188             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39189             this.setActivePanel(p);
39190         }
39191         panel.setRegion(null);
39192         if(this.activePanel == panel){
39193             this.activePanel = null;
39194         }
39195         if(this.config.autoDestroy !== false && preservePanel !== true){
39196             try{panel.destroy();}catch(e){}
39197         }
39198         this.fireEvent("panelremoved", this, panel);
39199         return panel;
39200     },
39201
39202     /**
39203      * Returns the TabPanel component used by this region
39204      * @return {Roo.TabPanel}
39205      */
39206     getTabs : function(){
39207         return this.tabs;
39208     },
39209
39210     createTool : function(parentEl, className){
39211         var btn = Roo.DomHelper.append(parentEl, {
39212             tag: "div",
39213             cls: "x-layout-tools-button",
39214             children: [ {
39215                 tag: "div",
39216                 cls: "roo-layout-tools-button-inner " + className,
39217                 html: "&#160;"
39218             }]
39219         }, true);
39220         btn.addClassOnOver("roo-layout-tools-button-over");
39221         return btn;
39222     }
39223 });/*
39224  * Based on:
39225  * Ext JS Library 1.1.1
39226  * Copyright(c) 2006-2007, Ext JS, LLC.
39227  *
39228  * Originally Released Under LGPL - original licence link has changed is not relivant.
39229  *
39230  * Fork - LGPL
39231  * <script type="text/javascript">
39232  */
39233  
39234
39235
39236 /**
39237  * @class Roo.SplitLayoutRegion
39238  * @extends Roo.LayoutRegion
39239  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39240  */
39241 Roo.bootstrap.layout.Split = function(config){
39242     this.cursor = config.cursor;
39243     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39244 };
39245
39246 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39247 {
39248     splitTip : "Drag to resize.",
39249     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39250     useSplitTips : false,
39251
39252     applyConfig : function(config){
39253         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39254     },
39255     
39256     onRender : function(ctr,pos) {
39257         
39258         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39259         if(!this.config.split){
39260             return;
39261         }
39262         if(!this.split){
39263             
39264             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39265                             tag: "div",
39266                             id: this.el.id + "-split",
39267                             cls: "roo-layout-split roo-layout-split-"+this.position,
39268                             html: "&#160;"
39269             });
39270             /** The SplitBar for this region 
39271             * @type Roo.SplitBar */
39272             // does not exist yet...
39273             Roo.log([this.position, this.orientation]);
39274             
39275             this.split = new Roo.bootstrap.SplitBar({
39276                 dragElement : splitEl,
39277                 resizingElement: this.el,
39278                 orientation : this.orientation
39279             });
39280             
39281             this.split.on("moved", this.onSplitMove, this);
39282             this.split.useShim = this.config.useShim === true;
39283             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39284             if(this.useSplitTips){
39285                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39286             }
39287             //if(config.collapsible){
39288             //    this.split.el.on("dblclick", this.collapse,  this);
39289             //}
39290         }
39291         if(typeof this.config.minSize != "undefined"){
39292             this.split.minSize = this.config.minSize;
39293         }
39294         if(typeof this.config.maxSize != "undefined"){
39295             this.split.maxSize = this.config.maxSize;
39296         }
39297         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39298             this.hideSplitter();
39299         }
39300         
39301     },
39302
39303     getHMaxSize : function(){
39304          var cmax = this.config.maxSize || 10000;
39305          var center = this.mgr.getRegion("center");
39306          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39307     },
39308
39309     getVMaxSize : function(){
39310          var cmax = this.config.maxSize || 10000;
39311          var center = this.mgr.getRegion("center");
39312          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39313     },
39314
39315     onSplitMove : function(split, newSize){
39316         this.fireEvent("resized", this, newSize);
39317     },
39318     
39319     /** 
39320      * Returns the {@link Roo.SplitBar} for this region.
39321      * @return {Roo.SplitBar}
39322      */
39323     getSplitBar : function(){
39324         return this.split;
39325     },
39326     
39327     hide : function(){
39328         this.hideSplitter();
39329         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39330     },
39331
39332     hideSplitter : function(){
39333         if(this.split){
39334             this.split.el.setLocation(-2000,-2000);
39335             this.split.el.hide();
39336         }
39337     },
39338
39339     show : function(){
39340         if(this.split){
39341             this.split.el.show();
39342         }
39343         Roo.bootstrap.layout.Split.superclass.show.call(this);
39344     },
39345     
39346     beforeSlide: function(){
39347         if(Roo.isGecko){// firefox overflow auto bug workaround
39348             this.bodyEl.clip();
39349             if(this.tabs) {
39350                 this.tabs.bodyEl.clip();
39351             }
39352             if(this.activePanel){
39353                 this.activePanel.getEl().clip();
39354                 
39355                 if(this.activePanel.beforeSlide){
39356                     this.activePanel.beforeSlide();
39357                 }
39358             }
39359         }
39360     },
39361     
39362     afterSlide : function(){
39363         if(Roo.isGecko){// firefox overflow auto bug workaround
39364             this.bodyEl.unclip();
39365             if(this.tabs) {
39366                 this.tabs.bodyEl.unclip();
39367             }
39368             if(this.activePanel){
39369                 this.activePanel.getEl().unclip();
39370                 if(this.activePanel.afterSlide){
39371                     this.activePanel.afterSlide();
39372                 }
39373             }
39374         }
39375     },
39376
39377     initAutoHide : function(){
39378         if(this.autoHide !== false){
39379             if(!this.autoHideHd){
39380                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39381                 this.autoHideHd = {
39382                     "mouseout": function(e){
39383                         if(!e.within(this.el, true)){
39384                             st.delay(500);
39385                         }
39386                     },
39387                     "mouseover" : function(e){
39388                         st.cancel();
39389                     },
39390                     scope : this
39391                 };
39392             }
39393             this.el.on(this.autoHideHd);
39394         }
39395     },
39396
39397     clearAutoHide : function(){
39398         if(this.autoHide !== false){
39399             this.el.un("mouseout", this.autoHideHd.mouseout);
39400             this.el.un("mouseover", this.autoHideHd.mouseover);
39401         }
39402     },
39403
39404     clearMonitor : function(){
39405         Roo.get(document).un("click", this.slideInIf, this);
39406     },
39407
39408     // these names are backwards but not changed for compat
39409     slideOut : function(){
39410         if(this.isSlid || this.el.hasActiveFx()){
39411             return;
39412         }
39413         this.isSlid = true;
39414         if(this.collapseBtn){
39415             this.collapseBtn.hide();
39416         }
39417         this.closeBtnState = this.closeBtn.getStyle('display');
39418         this.closeBtn.hide();
39419         if(this.stickBtn){
39420             this.stickBtn.show();
39421         }
39422         this.el.show();
39423         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39424         this.beforeSlide();
39425         this.el.setStyle("z-index", 10001);
39426         this.el.slideIn(this.getSlideAnchor(), {
39427             callback: function(){
39428                 this.afterSlide();
39429                 this.initAutoHide();
39430                 Roo.get(document).on("click", this.slideInIf, this);
39431                 this.fireEvent("slideshow", this);
39432             },
39433             scope: this,
39434             block: true
39435         });
39436     },
39437
39438     afterSlideIn : function(){
39439         this.clearAutoHide();
39440         this.isSlid = false;
39441         this.clearMonitor();
39442         this.el.setStyle("z-index", "");
39443         if(this.collapseBtn){
39444             this.collapseBtn.show();
39445         }
39446         this.closeBtn.setStyle('display', this.closeBtnState);
39447         if(this.stickBtn){
39448             this.stickBtn.hide();
39449         }
39450         this.fireEvent("slidehide", this);
39451     },
39452
39453     slideIn : function(cb){
39454         if(!this.isSlid || this.el.hasActiveFx()){
39455             Roo.callback(cb);
39456             return;
39457         }
39458         this.isSlid = false;
39459         this.beforeSlide();
39460         this.el.slideOut(this.getSlideAnchor(), {
39461             callback: function(){
39462                 this.el.setLeftTop(-10000, -10000);
39463                 this.afterSlide();
39464                 this.afterSlideIn();
39465                 Roo.callback(cb);
39466             },
39467             scope: this,
39468             block: true
39469         });
39470     },
39471     
39472     slideInIf : function(e){
39473         if(!e.within(this.el)){
39474             this.slideIn();
39475         }
39476     },
39477
39478     animateCollapse : function(){
39479         this.beforeSlide();
39480         this.el.setStyle("z-index", 20000);
39481         var anchor = this.getSlideAnchor();
39482         this.el.slideOut(anchor, {
39483             callback : function(){
39484                 this.el.setStyle("z-index", "");
39485                 this.collapsedEl.slideIn(anchor, {duration:.3});
39486                 this.afterSlide();
39487                 this.el.setLocation(-10000,-10000);
39488                 this.el.hide();
39489                 this.fireEvent("collapsed", this);
39490             },
39491             scope: this,
39492             block: true
39493         });
39494     },
39495
39496     animateExpand : function(){
39497         this.beforeSlide();
39498         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39499         this.el.setStyle("z-index", 20000);
39500         this.collapsedEl.hide({
39501             duration:.1
39502         });
39503         this.el.slideIn(this.getSlideAnchor(), {
39504             callback : function(){
39505                 this.el.setStyle("z-index", "");
39506                 this.afterSlide();
39507                 if(this.split){
39508                     this.split.el.show();
39509                 }
39510                 this.fireEvent("invalidated", this);
39511                 this.fireEvent("expanded", this);
39512             },
39513             scope: this,
39514             block: true
39515         });
39516     },
39517
39518     anchors : {
39519         "west" : "left",
39520         "east" : "right",
39521         "north" : "top",
39522         "south" : "bottom"
39523     },
39524
39525     sanchors : {
39526         "west" : "l",
39527         "east" : "r",
39528         "north" : "t",
39529         "south" : "b"
39530     },
39531
39532     canchors : {
39533         "west" : "tl-tr",
39534         "east" : "tr-tl",
39535         "north" : "tl-bl",
39536         "south" : "bl-tl"
39537     },
39538
39539     getAnchor : function(){
39540         return this.anchors[this.position];
39541     },
39542
39543     getCollapseAnchor : function(){
39544         return this.canchors[this.position];
39545     },
39546
39547     getSlideAnchor : function(){
39548         return this.sanchors[this.position];
39549     },
39550
39551     getAlignAdj : function(){
39552         var cm = this.cmargins;
39553         switch(this.position){
39554             case "west":
39555                 return [0, 0];
39556             break;
39557             case "east":
39558                 return [0, 0];
39559             break;
39560             case "north":
39561                 return [0, 0];
39562             break;
39563             case "south":
39564                 return [0, 0];
39565             break;
39566         }
39567     },
39568
39569     getExpandAdj : function(){
39570         var c = this.collapsedEl, cm = this.cmargins;
39571         switch(this.position){
39572             case "west":
39573                 return [-(cm.right+c.getWidth()+cm.left), 0];
39574             break;
39575             case "east":
39576                 return [cm.right+c.getWidth()+cm.left, 0];
39577             break;
39578             case "north":
39579                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39580             break;
39581             case "south":
39582                 return [0, cm.top+cm.bottom+c.getHeight()];
39583             break;
39584         }
39585     }
39586 });/*
39587  * Based on:
39588  * Ext JS Library 1.1.1
39589  * Copyright(c) 2006-2007, Ext JS, LLC.
39590  *
39591  * Originally Released Under LGPL - original licence link has changed is not relivant.
39592  *
39593  * Fork - LGPL
39594  * <script type="text/javascript">
39595  */
39596 /*
39597  * These classes are private internal classes
39598  */
39599 Roo.bootstrap.layout.Center = function(config){
39600     config.region = "center";
39601     Roo.bootstrap.layout.Region.call(this, config);
39602     this.visible = true;
39603     this.minWidth = config.minWidth || 20;
39604     this.minHeight = config.minHeight || 20;
39605 };
39606
39607 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39608     hide : function(){
39609         // center panel can't be hidden
39610     },
39611     
39612     show : function(){
39613         // center panel can't be hidden
39614     },
39615     
39616     getMinWidth: function(){
39617         return this.minWidth;
39618     },
39619     
39620     getMinHeight: function(){
39621         return this.minHeight;
39622     }
39623 });
39624
39625
39626
39627
39628  
39629
39630
39631
39632
39633
39634
39635 Roo.bootstrap.layout.North = function(config)
39636 {
39637     config.region = 'north';
39638     config.cursor = 'n-resize';
39639     
39640     Roo.bootstrap.layout.Split.call(this, config);
39641     
39642     
39643     if(this.split){
39644         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39645         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39646         this.split.el.addClass("roo-layout-split-v");
39647     }
39648     //var size = config.initialSize || config.height;
39649     //if(this.el && typeof size != "undefined"){
39650     //    this.el.setHeight(size);
39651     //}
39652 };
39653 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39654 {
39655     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39656      
39657      
39658     onRender : function(ctr, pos)
39659     {
39660         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39661         var size = this.config.initialSize || this.config.height;
39662         if(this.el && typeof size != "undefined"){
39663             this.el.setHeight(size);
39664         }
39665     
39666     },
39667     
39668     getBox : function(){
39669         if(this.collapsed){
39670             return this.collapsedEl.getBox();
39671         }
39672         var box = this.el.getBox();
39673         if(this.split){
39674             box.height += this.split.el.getHeight();
39675         }
39676         return box;
39677     },
39678     
39679     updateBox : function(box){
39680         if(this.split && !this.collapsed){
39681             box.height -= this.split.el.getHeight();
39682             this.split.el.setLeft(box.x);
39683             this.split.el.setTop(box.y+box.height);
39684             this.split.el.setWidth(box.width);
39685         }
39686         if(this.collapsed){
39687             this.updateBody(box.width, null);
39688         }
39689         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39690     }
39691 });
39692
39693
39694
39695
39696
39697 Roo.bootstrap.layout.South = function(config){
39698     config.region = 'south';
39699     config.cursor = 's-resize';
39700     Roo.bootstrap.layout.Split.call(this, config);
39701     if(this.split){
39702         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39703         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39704         this.split.el.addClass("roo-layout-split-v");
39705     }
39706     
39707 };
39708
39709 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39710     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39711     
39712     onRender : function(ctr, pos)
39713     {
39714         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39715         var size = this.config.initialSize || this.config.height;
39716         if(this.el && typeof size != "undefined"){
39717             this.el.setHeight(size);
39718         }
39719     
39720     },
39721     
39722     getBox : function(){
39723         if(this.collapsed){
39724             return this.collapsedEl.getBox();
39725         }
39726         var box = this.el.getBox();
39727         if(this.split){
39728             var sh = this.split.el.getHeight();
39729             box.height += sh;
39730             box.y -= sh;
39731         }
39732         return box;
39733     },
39734     
39735     updateBox : function(box){
39736         if(this.split && !this.collapsed){
39737             var sh = this.split.el.getHeight();
39738             box.height -= sh;
39739             box.y += sh;
39740             this.split.el.setLeft(box.x);
39741             this.split.el.setTop(box.y-sh);
39742             this.split.el.setWidth(box.width);
39743         }
39744         if(this.collapsed){
39745             this.updateBody(box.width, null);
39746         }
39747         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39748     }
39749 });
39750
39751 Roo.bootstrap.layout.East = function(config){
39752     config.region = "east";
39753     config.cursor = "e-resize";
39754     Roo.bootstrap.layout.Split.call(this, config);
39755     if(this.split){
39756         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39757         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39758         this.split.el.addClass("roo-layout-split-h");
39759     }
39760     
39761 };
39762 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39763     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39764     
39765     onRender : function(ctr, pos)
39766     {
39767         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39768         var size = this.config.initialSize || this.config.width;
39769         if(this.el && typeof size != "undefined"){
39770             this.el.setWidth(size);
39771         }
39772     
39773     },
39774     
39775     getBox : function(){
39776         if(this.collapsed){
39777             return this.collapsedEl.getBox();
39778         }
39779         var box = this.el.getBox();
39780         if(this.split){
39781             var sw = this.split.el.getWidth();
39782             box.width += sw;
39783             box.x -= sw;
39784         }
39785         return box;
39786     },
39787
39788     updateBox : function(box){
39789         if(this.split && !this.collapsed){
39790             var sw = this.split.el.getWidth();
39791             box.width -= sw;
39792             this.split.el.setLeft(box.x);
39793             this.split.el.setTop(box.y);
39794             this.split.el.setHeight(box.height);
39795             box.x += sw;
39796         }
39797         if(this.collapsed){
39798             this.updateBody(null, box.height);
39799         }
39800         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39801     }
39802 });
39803
39804 Roo.bootstrap.layout.West = function(config){
39805     config.region = "west";
39806     config.cursor = "w-resize";
39807     
39808     Roo.bootstrap.layout.Split.call(this, config);
39809     if(this.split){
39810         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39811         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39812         this.split.el.addClass("roo-layout-split-h");
39813     }
39814     
39815 };
39816 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39817     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39818     
39819     onRender: function(ctr, pos)
39820     {
39821         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39822         var size = this.config.initialSize || this.config.width;
39823         if(typeof size != "undefined"){
39824             this.el.setWidth(size);
39825         }
39826     },
39827     
39828     getBox : function(){
39829         if(this.collapsed){
39830             return this.collapsedEl.getBox();
39831         }
39832         var box = this.el.getBox();
39833         if (box.width == 0) {
39834             box.width = this.config.width; // kludge?
39835         }
39836         if(this.split){
39837             box.width += this.split.el.getWidth();
39838         }
39839         return box;
39840     },
39841     
39842     updateBox : function(box){
39843         if(this.split && !this.collapsed){
39844             var sw = this.split.el.getWidth();
39845             box.width -= sw;
39846             this.split.el.setLeft(box.x+box.width);
39847             this.split.el.setTop(box.y);
39848             this.split.el.setHeight(box.height);
39849         }
39850         if(this.collapsed){
39851             this.updateBody(null, box.height);
39852         }
39853         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39854     }
39855 });Roo.namespace("Roo.bootstrap.panel");/*
39856  * Based on:
39857  * Ext JS Library 1.1.1
39858  * Copyright(c) 2006-2007, Ext JS, LLC.
39859  *
39860  * Originally Released Under LGPL - original licence link has changed is not relivant.
39861  *
39862  * Fork - LGPL
39863  * <script type="text/javascript">
39864  */
39865 /**
39866  * @class Roo.ContentPanel
39867  * @extends Roo.util.Observable
39868  * A basic ContentPanel element.
39869  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39870  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39871  * @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
39872  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39873  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39874  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39875  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39876  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39877  * @cfg {String} title          The title for this panel
39878  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39879  * @cfg {String} url            Calls {@link #setUrl} with this value
39880  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39881  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39882  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39883  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39884  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39885  * @cfg {Boolean} badges render the badges
39886  * @cfg {String} cls  extra classes to use  
39887  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39888
39889  * @constructor
39890  * Create a new ContentPanel.
39891  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39892  * @param {String/Object} config A string to set only the title or a config object
39893  * @param {String} content (optional) Set the HTML content for this panel
39894  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39895  */
39896 Roo.bootstrap.panel.Content = function( config){
39897     
39898     this.tpl = config.tpl || false;
39899     
39900     var el = config.el;
39901     var content = config.content;
39902
39903     if(config.autoCreate){ // xtype is available if this is called from factory
39904         el = Roo.id();
39905     }
39906     this.el = Roo.get(el);
39907     if(!this.el && config && config.autoCreate){
39908         if(typeof config.autoCreate == "object"){
39909             if(!config.autoCreate.id){
39910                 config.autoCreate.id = config.id||el;
39911             }
39912             this.el = Roo.DomHelper.append(document.body,
39913                         config.autoCreate, true);
39914         }else{
39915             var elcfg =  {
39916                 tag: "div",
39917                 cls: (config.cls || '') +
39918                     (config.background ? ' bg-' + config.background : '') +
39919                     " roo-layout-inactive-content",
39920                 id: config.id||el
39921             };
39922             if (config.iframe) {
39923                 elcfg.cn = [
39924                     {
39925                         tag : 'iframe',
39926                         style : 'border: 0px',
39927                         src : 'about:blank'
39928                     }
39929                 ];
39930             }
39931               
39932             if (config.html) {
39933                 elcfg.html = config.html;
39934                 
39935             }
39936                         
39937             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39938             if (config.iframe) {
39939                 this.iframeEl = this.el.select('iframe',true).first();
39940             }
39941             
39942         }
39943     } 
39944     this.closable = false;
39945     this.loaded = false;
39946     this.active = false;
39947    
39948       
39949     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39950         
39951         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39952         
39953         this.wrapEl = this.el; //this.el.wrap();
39954         var ti = [];
39955         if (config.toolbar.items) {
39956             ti = config.toolbar.items ;
39957             delete config.toolbar.items ;
39958         }
39959         
39960         var nitems = [];
39961         this.toolbar.render(this.wrapEl, 'before');
39962         for(var i =0;i < ti.length;i++) {
39963           //  Roo.log(['add child', items[i]]);
39964             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39965         }
39966         this.toolbar.items = nitems;
39967         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39968         delete config.toolbar;
39969         
39970     }
39971     /*
39972     // xtype created footer. - not sure if will work as we normally have to render first..
39973     if (this.footer && !this.footer.el && this.footer.xtype) {
39974         if (!this.wrapEl) {
39975             this.wrapEl = this.el.wrap();
39976         }
39977     
39978         this.footer.container = this.wrapEl.createChild();
39979          
39980         this.footer = Roo.factory(this.footer, Roo);
39981         
39982     }
39983     */
39984     
39985      if(typeof config == "string"){
39986         this.title = config;
39987     }else{
39988         Roo.apply(this, config);
39989     }
39990     
39991     if(this.resizeEl){
39992         this.resizeEl = Roo.get(this.resizeEl, true);
39993     }else{
39994         this.resizeEl = this.el;
39995     }
39996     // handle view.xtype
39997     
39998  
39999     
40000     
40001     this.addEvents({
40002         /**
40003          * @event activate
40004          * Fires when this panel is activated. 
40005          * @param {Roo.ContentPanel} this
40006          */
40007         "activate" : true,
40008         /**
40009          * @event deactivate
40010          * Fires when this panel is activated. 
40011          * @param {Roo.ContentPanel} this
40012          */
40013         "deactivate" : true,
40014
40015         /**
40016          * @event resize
40017          * Fires when this panel is resized if fitToFrame is true.
40018          * @param {Roo.ContentPanel} this
40019          * @param {Number} width The width after any component adjustments
40020          * @param {Number} height The height after any component adjustments
40021          */
40022         "resize" : true,
40023         
40024          /**
40025          * @event render
40026          * Fires when this tab is created
40027          * @param {Roo.ContentPanel} this
40028          */
40029         "render" : true
40030         
40031         
40032         
40033     });
40034     
40035
40036     
40037     
40038     if(this.autoScroll && !this.iframe){
40039         this.resizeEl.setStyle("overflow", "auto");
40040     } else {
40041         // fix randome scrolling
40042         //this.el.on('scroll', function() {
40043         //    Roo.log('fix random scolling');
40044         //    this.scrollTo('top',0); 
40045         //});
40046     }
40047     content = content || this.content;
40048     if(content){
40049         this.setContent(content);
40050     }
40051     if(config && config.url){
40052         this.setUrl(this.url, this.params, this.loadOnce);
40053     }
40054     
40055     
40056     
40057     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40058     
40059     if (this.view && typeof(this.view.xtype) != 'undefined') {
40060         this.view.el = this.el.appendChild(document.createElement("div"));
40061         this.view = Roo.factory(this.view); 
40062         this.view.render  &&  this.view.render(false, '');  
40063     }
40064     
40065     
40066     this.fireEvent('render', this);
40067 };
40068
40069 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40070     
40071     cls : '',
40072     background : '',
40073     
40074     tabTip : '',
40075     
40076     iframe : false,
40077     iframeEl : false,
40078     
40079     setRegion : function(region){
40080         this.region = region;
40081         this.setActiveClass(region && !this.background);
40082     },
40083     
40084     
40085     setActiveClass: function(state)
40086     {
40087         if(state){
40088            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40089            this.el.setStyle('position','relative');
40090         }else{
40091            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40092            this.el.setStyle('position', 'absolute');
40093         } 
40094     },
40095     
40096     /**
40097      * Returns the toolbar for this Panel if one was configured. 
40098      * @return {Roo.Toolbar} 
40099      */
40100     getToolbar : function(){
40101         return this.toolbar;
40102     },
40103     
40104     setActiveState : function(active)
40105     {
40106         this.active = active;
40107         this.setActiveClass(active);
40108         if(!active){
40109             if(this.fireEvent("deactivate", this) === false){
40110                 return false;
40111             }
40112             return true;
40113         }
40114         this.fireEvent("activate", this);
40115         return true;
40116     },
40117     /**
40118      * Updates this panel's element (not for iframe)
40119      * @param {String} content The new content
40120      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40121     */
40122     setContent : function(content, loadScripts){
40123         if (this.iframe) {
40124             return;
40125         }
40126         
40127         this.el.update(content, loadScripts);
40128     },
40129
40130     ignoreResize : function(w, h){
40131         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40132             return true;
40133         }else{
40134             this.lastSize = {width: w, height: h};
40135             return false;
40136         }
40137     },
40138     /**
40139      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40140      * @return {Roo.UpdateManager} The UpdateManager
40141      */
40142     getUpdateManager : function(){
40143         if (this.iframe) {
40144             return false;
40145         }
40146         return this.el.getUpdateManager();
40147     },
40148      /**
40149      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40150      * Does not work with IFRAME contents
40151      * @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:
40152 <pre><code>
40153 panel.load({
40154     url: "your-url.php",
40155     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40156     callback: yourFunction,
40157     scope: yourObject, //(optional scope)
40158     discardUrl: false,
40159     nocache: false,
40160     text: "Loading...",
40161     timeout: 30,
40162     scripts: false
40163 });
40164 </code></pre>
40165      
40166      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40167      * 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.
40168      * @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}
40169      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40170      * @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.
40171      * @return {Roo.ContentPanel} this
40172      */
40173     load : function(){
40174         
40175         if (this.iframe) {
40176             return this;
40177         }
40178         
40179         var um = this.el.getUpdateManager();
40180         um.update.apply(um, arguments);
40181         return this;
40182     },
40183
40184
40185     /**
40186      * 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.
40187      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40188      * @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)
40189      * @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)
40190      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40191      */
40192     setUrl : function(url, params, loadOnce){
40193         if (this.iframe) {
40194             this.iframeEl.dom.src = url;
40195             return false;
40196         }
40197         
40198         if(this.refreshDelegate){
40199             this.removeListener("activate", this.refreshDelegate);
40200         }
40201         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40202         this.on("activate", this.refreshDelegate);
40203         return this.el.getUpdateManager();
40204     },
40205     
40206     _handleRefresh : function(url, params, loadOnce){
40207         if(!loadOnce || !this.loaded){
40208             var updater = this.el.getUpdateManager();
40209             updater.update(url, params, this._setLoaded.createDelegate(this));
40210         }
40211     },
40212     
40213     _setLoaded : function(){
40214         this.loaded = true;
40215     }, 
40216     
40217     /**
40218      * Returns this panel's id
40219      * @return {String} 
40220      */
40221     getId : function(){
40222         return this.el.id;
40223     },
40224     
40225     /** 
40226      * Returns this panel's element - used by regiosn to add.
40227      * @return {Roo.Element} 
40228      */
40229     getEl : function(){
40230         return this.wrapEl || this.el;
40231     },
40232     
40233    
40234     
40235     adjustForComponents : function(width, height)
40236     {
40237         //Roo.log('adjustForComponents ');
40238         if(this.resizeEl != this.el){
40239             width -= this.el.getFrameWidth('lr');
40240             height -= this.el.getFrameWidth('tb');
40241         }
40242         if(this.toolbar){
40243             var te = this.toolbar.getEl();
40244             te.setWidth(width);
40245             height -= te.getHeight();
40246         }
40247         if(this.footer){
40248             var te = this.footer.getEl();
40249             te.setWidth(width);
40250             height -= te.getHeight();
40251         }
40252         
40253         
40254         if(this.adjustments){
40255             width += this.adjustments[0];
40256             height += this.adjustments[1];
40257         }
40258         return {"width": width, "height": height};
40259     },
40260     
40261     setSize : function(width, height){
40262         if(this.fitToFrame && !this.ignoreResize(width, height)){
40263             if(this.fitContainer && this.resizeEl != this.el){
40264                 this.el.setSize(width, height);
40265             }
40266             var size = this.adjustForComponents(width, height);
40267             if (this.iframe) {
40268                 this.iframeEl.setSize(width,height);
40269             }
40270             
40271             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40272             this.fireEvent('resize', this, size.width, size.height);
40273             
40274             
40275         }
40276     },
40277     
40278     /**
40279      * Returns this panel's title
40280      * @return {String} 
40281      */
40282     getTitle : function(){
40283         
40284         if (typeof(this.title) != 'object') {
40285             return this.title;
40286         }
40287         
40288         var t = '';
40289         for (var k in this.title) {
40290             if (!this.title.hasOwnProperty(k)) {
40291                 continue;
40292             }
40293             
40294             if (k.indexOf('-') >= 0) {
40295                 var s = k.split('-');
40296                 for (var i = 0; i<s.length; i++) {
40297                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40298                 }
40299             } else {
40300                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40301             }
40302         }
40303         return t;
40304     },
40305     
40306     /**
40307      * Set this panel's title
40308      * @param {String} title
40309      */
40310     setTitle : function(title){
40311         this.title = title;
40312         if(this.region){
40313             this.region.updatePanelTitle(this, title);
40314         }
40315     },
40316     
40317     /**
40318      * Returns true is this panel was configured to be closable
40319      * @return {Boolean} 
40320      */
40321     isClosable : function(){
40322         return this.closable;
40323     },
40324     
40325     beforeSlide : function(){
40326         this.el.clip();
40327         this.resizeEl.clip();
40328     },
40329     
40330     afterSlide : function(){
40331         this.el.unclip();
40332         this.resizeEl.unclip();
40333     },
40334     
40335     /**
40336      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40337      *   Will fail silently if the {@link #setUrl} method has not been called.
40338      *   This does not activate the panel, just updates its content.
40339      */
40340     refresh : function(){
40341         if(this.refreshDelegate){
40342            this.loaded = false;
40343            this.refreshDelegate();
40344         }
40345     },
40346     
40347     /**
40348      * Destroys this panel
40349      */
40350     destroy : function(){
40351         this.el.removeAllListeners();
40352         var tempEl = document.createElement("span");
40353         tempEl.appendChild(this.el.dom);
40354         tempEl.innerHTML = "";
40355         this.el.remove();
40356         this.el = null;
40357     },
40358     
40359     /**
40360      * form - if the content panel contains a form - this is a reference to it.
40361      * @type {Roo.form.Form}
40362      */
40363     form : false,
40364     /**
40365      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40366      *    This contains a reference to it.
40367      * @type {Roo.View}
40368      */
40369     view : false,
40370     
40371       /**
40372      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40373      * <pre><code>
40374
40375 layout.addxtype({
40376        xtype : 'Form',
40377        items: [ .... ]
40378    }
40379 );
40380
40381 </code></pre>
40382      * @param {Object} cfg Xtype definition of item to add.
40383      */
40384     
40385     
40386     getChildContainer: function () {
40387         return this.getEl();
40388     }
40389     
40390     
40391     /*
40392         var  ret = new Roo.factory(cfg);
40393         return ret;
40394         
40395         
40396         // add form..
40397         if (cfg.xtype.match(/^Form$/)) {
40398             
40399             var el;
40400             //if (this.footer) {
40401             //    el = this.footer.container.insertSibling(false, 'before');
40402             //} else {
40403                 el = this.el.createChild();
40404             //}
40405
40406             this.form = new  Roo.form.Form(cfg);
40407             
40408             
40409             if ( this.form.allItems.length) {
40410                 this.form.render(el.dom);
40411             }
40412             return this.form;
40413         }
40414         // should only have one of theses..
40415         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40416             // views.. should not be just added - used named prop 'view''
40417             
40418             cfg.el = this.el.appendChild(document.createElement("div"));
40419             // factory?
40420             
40421             var ret = new Roo.factory(cfg);
40422              
40423              ret.render && ret.render(false, ''); // render blank..
40424             this.view = ret;
40425             return ret;
40426         }
40427         return false;
40428     }
40429     \*/
40430 });
40431  
40432 /**
40433  * @class Roo.bootstrap.panel.Grid
40434  * @extends Roo.bootstrap.panel.Content
40435  * @constructor
40436  * Create a new GridPanel.
40437  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40438  * @param {Object} config A the config object
40439   
40440  */
40441
40442
40443
40444 Roo.bootstrap.panel.Grid = function(config)
40445 {
40446     
40447       
40448     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40449         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40450
40451     config.el = this.wrapper;
40452     //this.el = this.wrapper;
40453     
40454       if (config.container) {
40455         // ctor'ed from a Border/panel.grid
40456         
40457         
40458         this.wrapper.setStyle("overflow", "hidden");
40459         this.wrapper.addClass('roo-grid-container');
40460
40461     }
40462     
40463     
40464     if(config.toolbar){
40465         var tool_el = this.wrapper.createChild();    
40466         this.toolbar = Roo.factory(config.toolbar);
40467         var ti = [];
40468         if (config.toolbar.items) {
40469             ti = config.toolbar.items ;
40470             delete config.toolbar.items ;
40471         }
40472         
40473         var nitems = [];
40474         this.toolbar.render(tool_el);
40475         for(var i =0;i < ti.length;i++) {
40476           //  Roo.log(['add child', items[i]]);
40477             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40478         }
40479         this.toolbar.items = nitems;
40480         
40481         delete config.toolbar;
40482     }
40483     
40484     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40485     config.grid.scrollBody = true;;
40486     config.grid.monitorWindowResize = false; // turn off autosizing
40487     config.grid.autoHeight = false;
40488     config.grid.autoWidth = false;
40489     
40490     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40491     
40492     if (config.background) {
40493         // render grid on panel activation (if panel background)
40494         this.on('activate', function(gp) {
40495             if (!gp.grid.rendered) {
40496                 gp.grid.render(this.wrapper);
40497                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40498             }
40499         });
40500             
40501     } else {
40502         this.grid.render(this.wrapper);
40503         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40504
40505     }
40506     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40507     // ??? needed ??? config.el = this.wrapper;
40508     
40509     
40510     
40511   
40512     // xtype created footer. - not sure if will work as we normally have to render first..
40513     if (this.footer && !this.footer.el && this.footer.xtype) {
40514         
40515         var ctr = this.grid.getView().getFooterPanel(true);
40516         this.footer.dataSource = this.grid.dataSource;
40517         this.footer = Roo.factory(this.footer, Roo);
40518         this.footer.render(ctr);
40519         
40520     }
40521     
40522     
40523     
40524     
40525      
40526 };
40527
40528 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40529     getId : function(){
40530         return this.grid.id;
40531     },
40532     
40533     /**
40534      * Returns the grid for this panel
40535      * @return {Roo.bootstrap.Table} 
40536      */
40537     getGrid : function(){
40538         return this.grid;    
40539     },
40540     
40541     setSize : function(width, height){
40542         if(!this.ignoreResize(width, height)){
40543             var grid = this.grid;
40544             var size = this.adjustForComponents(width, height);
40545             // tfoot is not a footer?
40546           
40547             
40548             var gridel = grid.getGridEl();
40549             gridel.setSize(size.width, size.height);
40550             
40551             var tbd = grid.getGridEl().select('tbody', true).first();
40552             var thd = grid.getGridEl().select('thead',true).first();
40553             var tbf= grid.getGridEl().select('tfoot', true).first();
40554
40555             if (tbf) {
40556                 size.height -= tbf.getHeight();
40557             }
40558             if (thd) {
40559                 size.height -= thd.getHeight();
40560             }
40561             
40562             tbd.setSize(size.width, size.height );
40563             // this is for the account management tab -seems to work there.
40564             var thd = grid.getGridEl().select('thead',true).first();
40565             //if (tbd) {
40566             //    tbd.setSize(size.width, size.height - thd.getHeight());
40567             //}
40568              
40569             grid.autoSize();
40570         }
40571     },
40572      
40573     
40574     
40575     beforeSlide : function(){
40576         this.grid.getView().scroller.clip();
40577     },
40578     
40579     afterSlide : function(){
40580         this.grid.getView().scroller.unclip();
40581     },
40582     
40583     destroy : function(){
40584         this.grid.destroy();
40585         delete this.grid;
40586         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40587     }
40588 });
40589
40590 /**
40591  * @class Roo.bootstrap.panel.Nest
40592  * @extends Roo.bootstrap.panel.Content
40593  * @constructor
40594  * Create a new Panel, that can contain a layout.Border.
40595  * 
40596  * 
40597  * @param {Roo.BorderLayout} layout The layout for this panel
40598  * @param {String/Object} config A string to set only the title or a config object
40599  */
40600 Roo.bootstrap.panel.Nest = function(config)
40601 {
40602     // construct with only one argument..
40603     /* FIXME - implement nicer consturctors
40604     if (layout.layout) {
40605         config = layout;
40606         layout = config.layout;
40607         delete config.layout;
40608     }
40609     if (layout.xtype && !layout.getEl) {
40610         // then layout needs constructing..
40611         layout = Roo.factory(layout, Roo);
40612     }
40613     */
40614     
40615     config.el =  config.layout.getEl();
40616     
40617     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40618     
40619     config.layout.monitorWindowResize = false; // turn off autosizing
40620     this.layout = config.layout;
40621     this.layout.getEl().addClass("roo-layout-nested-layout");
40622     this.layout.parent = this;
40623     
40624     
40625     
40626     
40627 };
40628
40629 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40630
40631     setSize : function(width, height){
40632         if(!this.ignoreResize(width, height)){
40633             var size = this.adjustForComponents(width, height);
40634             var el = this.layout.getEl();
40635             if (size.height < 1) {
40636                 el.setWidth(size.width);   
40637             } else {
40638                 el.setSize(size.width, size.height);
40639             }
40640             var touch = el.dom.offsetWidth;
40641             this.layout.layout();
40642             // ie requires a double layout on the first pass
40643             if(Roo.isIE && !this.initialized){
40644                 this.initialized = true;
40645                 this.layout.layout();
40646             }
40647         }
40648     },
40649     
40650     // activate all subpanels if not currently active..
40651     
40652     setActiveState : function(active){
40653         this.active = active;
40654         this.setActiveClass(active);
40655         
40656         if(!active){
40657             this.fireEvent("deactivate", this);
40658             return;
40659         }
40660         
40661         this.fireEvent("activate", this);
40662         // not sure if this should happen before or after..
40663         if (!this.layout) {
40664             return; // should not happen..
40665         }
40666         var reg = false;
40667         for (var r in this.layout.regions) {
40668             reg = this.layout.getRegion(r);
40669             if (reg.getActivePanel()) {
40670                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40671                 reg.setActivePanel(reg.getActivePanel());
40672                 continue;
40673             }
40674             if (!reg.panels.length) {
40675                 continue;
40676             }
40677             reg.showPanel(reg.getPanel(0));
40678         }
40679         
40680         
40681         
40682         
40683     },
40684     
40685     /**
40686      * Returns the nested BorderLayout for this panel
40687      * @return {Roo.BorderLayout} 
40688      */
40689     getLayout : function(){
40690         return this.layout;
40691     },
40692     
40693      /**
40694      * Adds a xtype elements to the layout of the nested panel
40695      * <pre><code>
40696
40697 panel.addxtype({
40698        xtype : 'ContentPanel',
40699        region: 'west',
40700        items: [ .... ]
40701    }
40702 );
40703
40704 panel.addxtype({
40705         xtype : 'NestedLayoutPanel',
40706         region: 'west',
40707         layout: {
40708            center: { },
40709            west: { }   
40710         },
40711         items : [ ... list of content panels or nested layout panels.. ]
40712    }
40713 );
40714 </code></pre>
40715      * @param {Object} cfg Xtype definition of item to add.
40716      */
40717     addxtype : function(cfg) {
40718         return this.layout.addxtype(cfg);
40719     
40720     }
40721 });/*
40722  * Based on:
40723  * Ext JS Library 1.1.1
40724  * Copyright(c) 2006-2007, Ext JS, LLC.
40725  *
40726  * Originally Released Under LGPL - original licence link has changed is not relivant.
40727  *
40728  * Fork - LGPL
40729  * <script type="text/javascript">
40730  */
40731 /**
40732  * @class Roo.TabPanel
40733  * @extends Roo.util.Observable
40734  * A lightweight tab container.
40735  * <br><br>
40736  * Usage:
40737  * <pre><code>
40738 // basic tabs 1, built from existing content
40739 var tabs = new Roo.TabPanel("tabs1");
40740 tabs.addTab("script", "View Script");
40741 tabs.addTab("markup", "View Markup");
40742 tabs.activate("script");
40743
40744 // more advanced tabs, built from javascript
40745 var jtabs = new Roo.TabPanel("jtabs");
40746 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40747
40748 // set up the UpdateManager
40749 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40750 var updater = tab2.getUpdateManager();
40751 updater.setDefaultUrl("ajax1.htm");
40752 tab2.on('activate', updater.refresh, updater, true);
40753
40754 // Use setUrl for Ajax loading
40755 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40756 tab3.setUrl("ajax2.htm", null, true);
40757
40758 // Disabled tab
40759 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40760 tab4.disable();
40761
40762 jtabs.activate("jtabs-1");
40763  * </code></pre>
40764  * @constructor
40765  * Create a new TabPanel.
40766  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40767  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40768  */
40769 Roo.bootstrap.panel.Tabs = function(config){
40770     /**
40771     * The container element for this TabPanel.
40772     * @type Roo.Element
40773     */
40774     this.el = Roo.get(config.el);
40775     delete config.el;
40776     if(config){
40777         if(typeof config == "boolean"){
40778             this.tabPosition = config ? "bottom" : "top";
40779         }else{
40780             Roo.apply(this, config);
40781         }
40782     }
40783     
40784     if(this.tabPosition == "bottom"){
40785         // if tabs are at the bottom = create the body first.
40786         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40787         this.el.addClass("roo-tabs-bottom");
40788     }
40789     // next create the tabs holders
40790     
40791     if (this.tabPosition == "west"){
40792         
40793         var reg = this.region; // fake it..
40794         while (reg) {
40795             if (!reg.mgr.parent) {
40796                 break;
40797             }
40798             reg = reg.mgr.parent.region;
40799         }
40800         Roo.log("got nest?");
40801         Roo.log(reg);
40802         if (reg.mgr.getRegion('west')) {
40803             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40804             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40805             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40806             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40807             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40808         
40809             
40810         }
40811         
40812         
40813     } else {
40814      
40815         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40816         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40817         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40818         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40819     }
40820     
40821     
40822     if(Roo.isIE){
40823         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40824     }
40825     
40826     // finally - if tabs are at the top, then create the body last..
40827     if(this.tabPosition != "bottom"){
40828         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40829          * @type Roo.Element
40830          */
40831         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40832         this.el.addClass("roo-tabs-top");
40833     }
40834     this.items = [];
40835
40836     this.bodyEl.setStyle("position", "relative");
40837
40838     this.active = null;
40839     this.activateDelegate = this.activate.createDelegate(this);
40840
40841     this.addEvents({
40842         /**
40843          * @event tabchange
40844          * Fires when the active tab changes
40845          * @param {Roo.TabPanel} this
40846          * @param {Roo.TabPanelItem} activePanel The new active tab
40847          */
40848         "tabchange": true,
40849         /**
40850          * @event beforetabchange
40851          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40852          * @param {Roo.TabPanel} this
40853          * @param {Object} e Set cancel to true on this object to cancel the tab change
40854          * @param {Roo.TabPanelItem} tab The tab being changed to
40855          */
40856         "beforetabchange" : true
40857     });
40858
40859     Roo.EventManager.onWindowResize(this.onResize, this);
40860     this.cpad = this.el.getPadding("lr");
40861     this.hiddenCount = 0;
40862
40863
40864     // toolbar on the tabbar support...
40865     if (this.toolbar) {
40866         alert("no toolbar support yet");
40867         this.toolbar  = false;
40868         /*
40869         var tcfg = this.toolbar;
40870         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40871         this.toolbar = new Roo.Toolbar(tcfg);
40872         if (Roo.isSafari) {
40873             var tbl = tcfg.container.child('table', true);
40874             tbl.setAttribute('width', '100%');
40875         }
40876         */
40877         
40878     }
40879    
40880
40881
40882     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40883 };
40884
40885 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40886     /*
40887      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40888      */
40889     tabPosition : "top",
40890     /*
40891      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40892      */
40893     currentTabWidth : 0,
40894     /*
40895      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40896      */
40897     minTabWidth : 40,
40898     /*
40899      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40900      */
40901     maxTabWidth : 250,
40902     /*
40903      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40904      */
40905     preferredTabWidth : 175,
40906     /*
40907      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40908      */
40909     resizeTabs : false,
40910     /*
40911      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40912      */
40913     monitorResize : true,
40914     /*
40915      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40916      */
40917     toolbar : false,  // set by caller..
40918     
40919     region : false, /// set by caller
40920     
40921     disableTooltips : true, // not used yet...
40922
40923     /**
40924      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40925      * @param {String} id The id of the div to use <b>or create</b>
40926      * @param {String} text The text for the tab
40927      * @param {String} content (optional) Content to put in the TabPanelItem body
40928      * @param {Boolean} closable (optional) True to create a close icon on the tab
40929      * @return {Roo.TabPanelItem} The created TabPanelItem
40930      */
40931     addTab : function(id, text, content, closable, tpl)
40932     {
40933         var item = new Roo.bootstrap.panel.TabItem({
40934             panel: this,
40935             id : id,
40936             text : text,
40937             closable : closable,
40938             tpl : tpl
40939         });
40940         this.addTabItem(item);
40941         if(content){
40942             item.setContent(content);
40943         }
40944         return item;
40945     },
40946
40947     /**
40948      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40949      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40950      * @return {Roo.TabPanelItem}
40951      */
40952     getTab : function(id){
40953         return this.items[id];
40954     },
40955
40956     /**
40957      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40958      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40959      */
40960     hideTab : function(id){
40961         var t = this.items[id];
40962         if(!t.isHidden()){
40963            t.setHidden(true);
40964            this.hiddenCount++;
40965            this.autoSizeTabs();
40966         }
40967     },
40968
40969     /**
40970      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40971      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40972      */
40973     unhideTab : function(id){
40974         var t = this.items[id];
40975         if(t.isHidden()){
40976            t.setHidden(false);
40977            this.hiddenCount--;
40978            this.autoSizeTabs();
40979         }
40980     },
40981
40982     /**
40983      * Adds an existing {@link Roo.TabPanelItem}.
40984      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40985      */
40986     addTabItem : function(item)
40987     {
40988         this.items[item.id] = item;
40989         this.items.push(item);
40990         this.autoSizeTabs();
40991       //  if(this.resizeTabs){
40992     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40993   //         this.autoSizeTabs();
40994 //        }else{
40995 //            item.autoSize();
40996        // }
40997     },
40998
40999     /**
41000      * Removes a {@link Roo.TabPanelItem}.
41001      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41002      */
41003     removeTab : function(id){
41004         var items = this.items;
41005         var tab = items[id];
41006         if(!tab) { return; }
41007         var index = items.indexOf(tab);
41008         if(this.active == tab && items.length > 1){
41009             var newTab = this.getNextAvailable(index);
41010             if(newTab) {
41011                 newTab.activate();
41012             }
41013         }
41014         this.stripEl.dom.removeChild(tab.pnode.dom);
41015         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41016             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41017         }
41018         items.splice(index, 1);
41019         delete this.items[tab.id];
41020         tab.fireEvent("close", tab);
41021         tab.purgeListeners();
41022         this.autoSizeTabs();
41023     },
41024
41025     getNextAvailable : function(start){
41026         var items = this.items;
41027         var index = start;
41028         // look for a next tab that will slide over to
41029         // replace the one being removed
41030         while(index < items.length){
41031             var item = items[++index];
41032             if(item && !item.isHidden()){
41033                 return item;
41034             }
41035         }
41036         // if one isn't found select the previous tab (on the left)
41037         index = start;
41038         while(index >= 0){
41039             var item = items[--index];
41040             if(item && !item.isHidden()){
41041                 return item;
41042             }
41043         }
41044         return null;
41045     },
41046
41047     /**
41048      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41049      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41050      */
41051     disableTab : function(id){
41052         var tab = this.items[id];
41053         if(tab && this.active != tab){
41054             tab.disable();
41055         }
41056     },
41057
41058     /**
41059      * Enables a {@link Roo.TabPanelItem} that is disabled.
41060      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41061      */
41062     enableTab : function(id){
41063         var tab = this.items[id];
41064         tab.enable();
41065     },
41066
41067     /**
41068      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41069      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41070      * @return {Roo.TabPanelItem} The TabPanelItem.
41071      */
41072     activate : function(id)
41073     {
41074         //Roo.log('activite:'  + id);
41075         
41076         var tab = this.items[id];
41077         if(!tab){
41078             return null;
41079         }
41080         if(tab == this.active || tab.disabled){
41081             return tab;
41082         }
41083         var e = {};
41084         this.fireEvent("beforetabchange", this, e, tab);
41085         if(e.cancel !== true && !tab.disabled){
41086             if(this.active){
41087                 this.active.hide();
41088             }
41089             this.active = this.items[id];
41090             this.active.show();
41091             this.fireEvent("tabchange", this, this.active);
41092         }
41093         return tab;
41094     },
41095
41096     /**
41097      * Gets the active {@link Roo.TabPanelItem}.
41098      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41099      */
41100     getActiveTab : function(){
41101         return this.active;
41102     },
41103
41104     /**
41105      * Updates the tab body element to fit the height of the container element
41106      * for overflow scrolling
41107      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41108      */
41109     syncHeight : function(targetHeight){
41110         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41111         var bm = this.bodyEl.getMargins();
41112         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41113         this.bodyEl.setHeight(newHeight);
41114         return newHeight;
41115     },
41116
41117     onResize : function(){
41118         if(this.monitorResize){
41119             this.autoSizeTabs();
41120         }
41121     },
41122
41123     /**
41124      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41125      */
41126     beginUpdate : function(){
41127         this.updating = true;
41128     },
41129
41130     /**
41131      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41132      */
41133     endUpdate : function(){
41134         this.updating = false;
41135         this.autoSizeTabs();
41136     },
41137
41138     /**
41139      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41140      */
41141     autoSizeTabs : function()
41142     {
41143         var count = this.items.length;
41144         var vcount = count - this.hiddenCount;
41145         
41146         if (vcount < 2) {
41147             this.stripEl.hide();
41148         } else {
41149             this.stripEl.show();
41150         }
41151         
41152         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41153             return;
41154         }
41155         
41156         
41157         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41158         var availWidth = Math.floor(w / vcount);
41159         var b = this.stripBody;
41160         if(b.getWidth() > w){
41161             var tabs = this.items;
41162             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41163             if(availWidth < this.minTabWidth){
41164                 /*if(!this.sleft){    // incomplete scrolling code
41165                     this.createScrollButtons();
41166                 }
41167                 this.showScroll();
41168                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41169             }
41170         }else{
41171             if(this.currentTabWidth < this.preferredTabWidth){
41172                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41173             }
41174         }
41175     },
41176
41177     /**
41178      * Returns the number of tabs in this TabPanel.
41179      * @return {Number}
41180      */
41181      getCount : function(){
41182          return this.items.length;
41183      },
41184
41185     /**
41186      * Resizes all the tabs to the passed width
41187      * @param {Number} The new width
41188      */
41189     setTabWidth : function(width){
41190         this.currentTabWidth = width;
41191         for(var i = 0, len = this.items.length; i < len; i++) {
41192                 if(!this.items[i].isHidden()) {
41193                 this.items[i].setWidth(width);
41194             }
41195         }
41196     },
41197
41198     /**
41199      * Destroys this TabPanel
41200      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41201      */
41202     destroy : function(removeEl){
41203         Roo.EventManager.removeResizeListener(this.onResize, this);
41204         for(var i = 0, len = this.items.length; i < len; i++){
41205             this.items[i].purgeListeners();
41206         }
41207         if(removeEl === true){
41208             this.el.update("");
41209             this.el.remove();
41210         }
41211     },
41212     
41213     createStrip : function(container)
41214     {
41215         var strip = document.createElement("nav");
41216         strip.className = Roo.bootstrap.version == 4 ?
41217             "navbar-light bg-light" : 
41218             "navbar navbar-default"; //"x-tabs-wrap";
41219         container.appendChild(strip);
41220         return strip;
41221     },
41222     
41223     createStripList : function(strip)
41224     {
41225         // div wrapper for retard IE
41226         // returns the "tr" element.
41227         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41228         //'<div class="x-tabs-strip-wrap">'+
41229           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41230           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41231         return strip.firstChild; //.firstChild.firstChild.firstChild;
41232     },
41233     createBody : function(container)
41234     {
41235         var body = document.createElement("div");
41236         Roo.id(body, "tab-body");
41237         //Roo.fly(body).addClass("x-tabs-body");
41238         Roo.fly(body).addClass("tab-content");
41239         container.appendChild(body);
41240         return body;
41241     },
41242     createItemBody :function(bodyEl, id){
41243         var body = Roo.getDom(id);
41244         if(!body){
41245             body = document.createElement("div");
41246             body.id = id;
41247         }
41248         //Roo.fly(body).addClass("x-tabs-item-body");
41249         Roo.fly(body).addClass("tab-pane");
41250          bodyEl.insertBefore(body, bodyEl.firstChild);
41251         return body;
41252     },
41253     /** @private */
41254     createStripElements :  function(stripEl, text, closable, tpl)
41255     {
41256         var td = document.createElement("li"); // was td..
41257         td.className = 'nav-item';
41258         
41259         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41260         
41261         
41262         stripEl.appendChild(td);
41263         /*if(closable){
41264             td.className = "x-tabs-closable";
41265             if(!this.closeTpl){
41266                 this.closeTpl = new Roo.Template(
41267                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41268                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41269                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41270                 );
41271             }
41272             var el = this.closeTpl.overwrite(td, {"text": text});
41273             var close = el.getElementsByTagName("div")[0];
41274             var inner = el.getElementsByTagName("em")[0];
41275             return {"el": el, "close": close, "inner": inner};
41276         } else {
41277         */
41278         // not sure what this is..
41279 //            if(!this.tabTpl){
41280                 //this.tabTpl = new Roo.Template(
41281                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41282                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41283                 //);
41284 //                this.tabTpl = new Roo.Template(
41285 //                   '<a href="#">' +
41286 //                   '<span unselectable="on"' +
41287 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41288 //                            ' >{text}</span></a>'
41289 //                );
41290 //                
41291 //            }
41292
41293
41294             var template = tpl || this.tabTpl || false;
41295             
41296             if(!template){
41297                 template =  new Roo.Template(
41298                         Roo.bootstrap.version == 4 ? 
41299                             (
41300                                 '<a class="nav-link" href="#" unselectable="on"' +
41301                                      (this.disableTooltips ? '' : ' title="{text}"') +
41302                                      ' >{text}</a>'
41303                             ) : (
41304                                 '<a class="nav-link" href="#">' +
41305                                 '<span unselectable="on"' +
41306                                          (this.disableTooltips ? '' : ' title="{text}"') +
41307                                     ' >{text}</span></a>'
41308                             )
41309                 );
41310             }
41311             
41312             switch (typeof(template)) {
41313                 case 'object' :
41314                     break;
41315                 case 'string' :
41316                     template = new Roo.Template(template);
41317                     break;
41318                 default :
41319                     break;
41320             }
41321             
41322             var el = template.overwrite(td, {"text": text});
41323             
41324             var inner = el.getElementsByTagName("span")[0];
41325             
41326             return {"el": el, "inner": inner};
41327             
41328     }
41329         
41330     
41331 });
41332
41333 /**
41334  * @class Roo.TabPanelItem
41335  * @extends Roo.util.Observable
41336  * Represents an individual item (tab plus body) in a TabPanel.
41337  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41338  * @param {String} id The id of this TabPanelItem
41339  * @param {String} text The text for the tab of this TabPanelItem
41340  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41341  */
41342 Roo.bootstrap.panel.TabItem = function(config){
41343     /**
41344      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41345      * @type Roo.TabPanel
41346      */
41347     this.tabPanel = config.panel;
41348     /**
41349      * The id for this TabPanelItem
41350      * @type String
41351      */
41352     this.id = config.id;
41353     /** @private */
41354     this.disabled = false;
41355     /** @private */
41356     this.text = config.text;
41357     /** @private */
41358     this.loaded = false;
41359     this.closable = config.closable;
41360
41361     /**
41362      * The body element for this TabPanelItem.
41363      * @type Roo.Element
41364      */
41365     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41366     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41367     this.bodyEl.setStyle("display", "block");
41368     this.bodyEl.setStyle("zoom", "1");
41369     //this.hideAction();
41370
41371     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41372     /** @private */
41373     this.el = Roo.get(els.el);
41374     this.inner = Roo.get(els.inner, true);
41375      this.textEl = Roo.bootstrap.version == 4 ?
41376         this.el : Roo.get(this.el.dom.firstChild, true);
41377
41378     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41379     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41380
41381     
41382 //    this.el.on("mousedown", this.onTabMouseDown, this);
41383     this.el.on("click", this.onTabClick, this);
41384     /** @private */
41385     if(config.closable){
41386         var c = Roo.get(els.close, true);
41387         c.dom.title = this.closeText;
41388         c.addClassOnOver("close-over");
41389         c.on("click", this.closeClick, this);
41390      }
41391
41392     this.addEvents({
41393          /**
41394          * @event activate
41395          * Fires when this tab becomes the active tab.
41396          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41397          * @param {Roo.TabPanelItem} this
41398          */
41399         "activate": true,
41400         /**
41401          * @event beforeclose
41402          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41403          * @param {Roo.TabPanelItem} this
41404          * @param {Object} e Set cancel to true on this object to cancel the close.
41405          */
41406         "beforeclose": true,
41407         /**
41408          * @event close
41409          * Fires when this tab is closed.
41410          * @param {Roo.TabPanelItem} this
41411          */
41412          "close": true,
41413         /**
41414          * @event deactivate
41415          * Fires when this tab is no longer the active tab.
41416          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41417          * @param {Roo.TabPanelItem} this
41418          */
41419          "deactivate" : true
41420     });
41421     this.hidden = false;
41422
41423     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41424 };
41425
41426 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41427            {
41428     purgeListeners : function(){
41429        Roo.util.Observable.prototype.purgeListeners.call(this);
41430        this.el.removeAllListeners();
41431     },
41432     /**
41433      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41434      */
41435     show : function(){
41436         this.status_node.addClass("active");
41437         this.showAction();
41438         if(Roo.isOpera){
41439             this.tabPanel.stripWrap.repaint();
41440         }
41441         this.fireEvent("activate", this.tabPanel, this);
41442     },
41443
41444     /**
41445      * Returns true if this tab is the active tab.
41446      * @return {Boolean}
41447      */
41448     isActive : function(){
41449         return this.tabPanel.getActiveTab() == this;
41450     },
41451
41452     /**
41453      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41454      */
41455     hide : function(){
41456         this.status_node.removeClass("active");
41457         this.hideAction();
41458         this.fireEvent("deactivate", this.tabPanel, this);
41459     },
41460
41461     hideAction : function(){
41462         this.bodyEl.hide();
41463         this.bodyEl.setStyle("position", "absolute");
41464         this.bodyEl.setLeft("-20000px");
41465         this.bodyEl.setTop("-20000px");
41466     },
41467
41468     showAction : function(){
41469         this.bodyEl.setStyle("position", "relative");
41470         this.bodyEl.setTop("");
41471         this.bodyEl.setLeft("");
41472         this.bodyEl.show();
41473     },
41474
41475     /**
41476      * Set the tooltip for the tab.
41477      * @param {String} tooltip The tab's tooltip
41478      */
41479     setTooltip : function(text){
41480         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41481             this.textEl.dom.qtip = text;
41482             this.textEl.dom.removeAttribute('title');
41483         }else{
41484             this.textEl.dom.title = text;
41485         }
41486     },
41487
41488     onTabClick : function(e){
41489         e.preventDefault();
41490         this.tabPanel.activate(this.id);
41491     },
41492
41493     onTabMouseDown : function(e){
41494         e.preventDefault();
41495         this.tabPanel.activate(this.id);
41496     },
41497 /*
41498     getWidth : function(){
41499         return this.inner.getWidth();
41500     },
41501
41502     setWidth : function(width){
41503         var iwidth = width - this.linode.getPadding("lr");
41504         this.inner.setWidth(iwidth);
41505         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41506         this.linode.setWidth(width);
41507     },
41508 */
41509     /**
41510      * Show or hide the tab
41511      * @param {Boolean} hidden True to hide or false to show.
41512      */
41513     setHidden : function(hidden){
41514         this.hidden = hidden;
41515         this.linode.setStyle("display", hidden ? "none" : "");
41516     },
41517
41518     /**
41519      * Returns true if this tab is "hidden"
41520      * @return {Boolean}
41521      */
41522     isHidden : function(){
41523         return this.hidden;
41524     },
41525
41526     /**
41527      * Returns the text for this tab
41528      * @return {String}
41529      */
41530     getText : function(){
41531         return this.text;
41532     },
41533     /*
41534     autoSize : function(){
41535         //this.el.beginMeasure();
41536         this.textEl.setWidth(1);
41537         /*
41538          *  #2804 [new] Tabs in Roojs
41539          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41540          */
41541         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41542         //this.el.endMeasure();
41543     //},
41544
41545     /**
41546      * Sets the text for the tab (Note: this also sets the tooltip text)
41547      * @param {String} text The tab's text and tooltip
41548      */
41549     setText : function(text){
41550         this.text = text;
41551         this.textEl.update(text);
41552         this.setTooltip(text);
41553         //if(!this.tabPanel.resizeTabs){
41554         //    this.autoSize();
41555         //}
41556     },
41557     /**
41558      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41559      */
41560     activate : function(){
41561         this.tabPanel.activate(this.id);
41562     },
41563
41564     /**
41565      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41566      */
41567     disable : function(){
41568         if(this.tabPanel.active != this){
41569             this.disabled = true;
41570             this.status_node.addClass("disabled");
41571         }
41572     },
41573
41574     /**
41575      * Enables this TabPanelItem if it was previously disabled.
41576      */
41577     enable : function(){
41578         this.disabled = false;
41579         this.status_node.removeClass("disabled");
41580     },
41581
41582     /**
41583      * Sets the content for this TabPanelItem.
41584      * @param {String} content The content
41585      * @param {Boolean} loadScripts true to look for and load scripts
41586      */
41587     setContent : function(content, loadScripts){
41588         this.bodyEl.update(content, loadScripts);
41589     },
41590
41591     /**
41592      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41593      * @return {Roo.UpdateManager} The UpdateManager
41594      */
41595     getUpdateManager : function(){
41596         return this.bodyEl.getUpdateManager();
41597     },
41598
41599     /**
41600      * Set a URL to be used to load the content for this TabPanelItem.
41601      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41602      * @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)
41603      * @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)
41604      * @return {Roo.UpdateManager} The UpdateManager
41605      */
41606     setUrl : function(url, params, loadOnce){
41607         if(this.refreshDelegate){
41608             this.un('activate', this.refreshDelegate);
41609         }
41610         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41611         this.on("activate", this.refreshDelegate);
41612         return this.bodyEl.getUpdateManager();
41613     },
41614
41615     /** @private */
41616     _handleRefresh : function(url, params, loadOnce){
41617         if(!loadOnce || !this.loaded){
41618             var updater = this.bodyEl.getUpdateManager();
41619             updater.update(url, params, this._setLoaded.createDelegate(this));
41620         }
41621     },
41622
41623     /**
41624      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41625      *   Will fail silently if the setUrl method has not been called.
41626      *   This does not activate the panel, just updates its content.
41627      */
41628     refresh : function(){
41629         if(this.refreshDelegate){
41630            this.loaded = false;
41631            this.refreshDelegate();
41632         }
41633     },
41634
41635     /** @private */
41636     _setLoaded : function(){
41637         this.loaded = true;
41638     },
41639
41640     /** @private */
41641     closeClick : function(e){
41642         var o = {};
41643         e.stopEvent();
41644         this.fireEvent("beforeclose", this, o);
41645         if(o.cancel !== true){
41646             this.tabPanel.removeTab(this.id);
41647         }
41648     },
41649     /**
41650      * The text displayed in the tooltip for the close icon.
41651      * @type String
41652      */
41653     closeText : "Close this tab"
41654 });
41655 /**
41656 *    This script refer to:
41657 *    Title: International Telephone Input
41658 *    Author: Jack O'Connor
41659 *    Code version:  v12.1.12
41660 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41661 **/
41662
41663 Roo.bootstrap.PhoneInputData = function() {
41664     var d = [
41665       [
41666         "Afghanistan (‫افغانستان‬‎)",
41667         "af",
41668         "93"
41669       ],
41670       [
41671         "Albania (Shqipëri)",
41672         "al",
41673         "355"
41674       ],
41675       [
41676         "Algeria (‫الجزائر‬‎)",
41677         "dz",
41678         "213"
41679       ],
41680       [
41681         "American Samoa",
41682         "as",
41683         "1684"
41684       ],
41685       [
41686         "Andorra",
41687         "ad",
41688         "376"
41689       ],
41690       [
41691         "Angola",
41692         "ao",
41693         "244"
41694       ],
41695       [
41696         "Anguilla",
41697         "ai",
41698         "1264"
41699       ],
41700       [
41701         "Antigua and Barbuda",
41702         "ag",
41703         "1268"
41704       ],
41705       [
41706         "Argentina",
41707         "ar",
41708         "54"
41709       ],
41710       [
41711         "Armenia (Հայաստան)",
41712         "am",
41713         "374"
41714       ],
41715       [
41716         "Aruba",
41717         "aw",
41718         "297"
41719       ],
41720       [
41721         "Australia",
41722         "au",
41723         "61",
41724         0
41725       ],
41726       [
41727         "Austria (Österreich)",
41728         "at",
41729         "43"
41730       ],
41731       [
41732         "Azerbaijan (Azərbaycan)",
41733         "az",
41734         "994"
41735       ],
41736       [
41737         "Bahamas",
41738         "bs",
41739         "1242"
41740       ],
41741       [
41742         "Bahrain (‫البحرين‬‎)",
41743         "bh",
41744         "973"
41745       ],
41746       [
41747         "Bangladesh (বাংলাদেশ)",
41748         "bd",
41749         "880"
41750       ],
41751       [
41752         "Barbados",
41753         "bb",
41754         "1246"
41755       ],
41756       [
41757         "Belarus (Беларусь)",
41758         "by",
41759         "375"
41760       ],
41761       [
41762         "Belgium (België)",
41763         "be",
41764         "32"
41765       ],
41766       [
41767         "Belize",
41768         "bz",
41769         "501"
41770       ],
41771       [
41772         "Benin (Bénin)",
41773         "bj",
41774         "229"
41775       ],
41776       [
41777         "Bermuda",
41778         "bm",
41779         "1441"
41780       ],
41781       [
41782         "Bhutan (འབྲུག)",
41783         "bt",
41784         "975"
41785       ],
41786       [
41787         "Bolivia",
41788         "bo",
41789         "591"
41790       ],
41791       [
41792         "Bosnia and Herzegovina (Босна и Херцеговина)",
41793         "ba",
41794         "387"
41795       ],
41796       [
41797         "Botswana",
41798         "bw",
41799         "267"
41800       ],
41801       [
41802         "Brazil (Brasil)",
41803         "br",
41804         "55"
41805       ],
41806       [
41807         "British Indian Ocean Territory",
41808         "io",
41809         "246"
41810       ],
41811       [
41812         "British Virgin Islands",
41813         "vg",
41814         "1284"
41815       ],
41816       [
41817         "Brunei",
41818         "bn",
41819         "673"
41820       ],
41821       [
41822         "Bulgaria (България)",
41823         "bg",
41824         "359"
41825       ],
41826       [
41827         "Burkina Faso",
41828         "bf",
41829         "226"
41830       ],
41831       [
41832         "Burundi (Uburundi)",
41833         "bi",
41834         "257"
41835       ],
41836       [
41837         "Cambodia (កម្ពុជា)",
41838         "kh",
41839         "855"
41840       ],
41841       [
41842         "Cameroon (Cameroun)",
41843         "cm",
41844         "237"
41845       ],
41846       [
41847         "Canada",
41848         "ca",
41849         "1",
41850         1,
41851         ["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"]
41852       ],
41853       [
41854         "Cape Verde (Kabu Verdi)",
41855         "cv",
41856         "238"
41857       ],
41858       [
41859         "Caribbean Netherlands",
41860         "bq",
41861         "599",
41862         1
41863       ],
41864       [
41865         "Cayman Islands",
41866         "ky",
41867         "1345"
41868       ],
41869       [
41870         "Central African Republic (République centrafricaine)",
41871         "cf",
41872         "236"
41873       ],
41874       [
41875         "Chad (Tchad)",
41876         "td",
41877         "235"
41878       ],
41879       [
41880         "Chile",
41881         "cl",
41882         "56"
41883       ],
41884       [
41885         "China (中国)",
41886         "cn",
41887         "86"
41888       ],
41889       [
41890         "Christmas Island",
41891         "cx",
41892         "61",
41893         2
41894       ],
41895       [
41896         "Cocos (Keeling) Islands",
41897         "cc",
41898         "61",
41899         1
41900       ],
41901       [
41902         "Colombia",
41903         "co",
41904         "57"
41905       ],
41906       [
41907         "Comoros (‫جزر القمر‬‎)",
41908         "km",
41909         "269"
41910       ],
41911       [
41912         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41913         "cd",
41914         "243"
41915       ],
41916       [
41917         "Congo (Republic) (Congo-Brazzaville)",
41918         "cg",
41919         "242"
41920       ],
41921       [
41922         "Cook Islands",
41923         "ck",
41924         "682"
41925       ],
41926       [
41927         "Costa Rica",
41928         "cr",
41929         "506"
41930       ],
41931       [
41932         "Côte d’Ivoire",
41933         "ci",
41934         "225"
41935       ],
41936       [
41937         "Croatia (Hrvatska)",
41938         "hr",
41939         "385"
41940       ],
41941       [
41942         "Cuba",
41943         "cu",
41944         "53"
41945       ],
41946       [
41947         "Curaçao",
41948         "cw",
41949         "599",
41950         0
41951       ],
41952       [
41953         "Cyprus (Κύπρος)",
41954         "cy",
41955         "357"
41956       ],
41957       [
41958         "Czech Republic (Česká republika)",
41959         "cz",
41960         "420"
41961       ],
41962       [
41963         "Denmark (Danmark)",
41964         "dk",
41965         "45"
41966       ],
41967       [
41968         "Djibouti",
41969         "dj",
41970         "253"
41971       ],
41972       [
41973         "Dominica",
41974         "dm",
41975         "1767"
41976       ],
41977       [
41978         "Dominican Republic (República Dominicana)",
41979         "do",
41980         "1",
41981         2,
41982         ["809", "829", "849"]
41983       ],
41984       [
41985         "Ecuador",
41986         "ec",
41987         "593"
41988       ],
41989       [
41990         "Egypt (‫مصر‬‎)",
41991         "eg",
41992         "20"
41993       ],
41994       [
41995         "El Salvador",
41996         "sv",
41997         "503"
41998       ],
41999       [
42000         "Equatorial Guinea (Guinea Ecuatorial)",
42001         "gq",
42002         "240"
42003       ],
42004       [
42005         "Eritrea",
42006         "er",
42007         "291"
42008       ],
42009       [
42010         "Estonia (Eesti)",
42011         "ee",
42012         "372"
42013       ],
42014       [
42015         "Ethiopia",
42016         "et",
42017         "251"
42018       ],
42019       [
42020         "Falkland Islands (Islas Malvinas)",
42021         "fk",
42022         "500"
42023       ],
42024       [
42025         "Faroe Islands (Føroyar)",
42026         "fo",
42027         "298"
42028       ],
42029       [
42030         "Fiji",
42031         "fj",
42032         "679"
42033       ],
42034       [
42035         "Finland (Suomi)",
42036         "fi",
42037         "358",
42038         0
42039       ],
42040       [
42041         "France",
42042         "fr",
42043         "33"
42044       ],
42045       [
42046         "French Guiana (Guyane française)",
42047         "gf",
42048         "594"
42049       ],
42050       [
42051         "French Polynesia (Polynésie française)",
42052         "pf",
42053         "689"
42054       ],
42055       [
42056         "Gabon",
42057         "ga",
42058         "241"
42059       ],
42060       [
42061         "Gambia",
42062         "gm",
42063         "220"
42064       ],
42065       [
42066         "Georgia (საქართველო)",
42067         "ge",
42068         "995"
42069       ],
42070       [
42071         "Germany (Deutschland)",
42072         "de",
42073         "49"
42074       ],
42075       [
42076         "Ghana (Gaana)",
42077         "gh",
42078         "233"
42079       ],
42080       [
42081         "Gibraltar",
42082         "gi",
42083         "350"
42084       ],
42085       [
42086         "Greece (Ελλάδα)",
42087         "gr",
42088         "30"
42089       ],
42090       [
42091         "Greenland (Kalaallit Nunaat)",
42092         "gl",
42093         "299"
42094       ],
42095       [
42096         "Grenada",
42097         "gd",
42098         "1473"
42099       ],
42100       [
42101         "Guadeloupe",
42102         "gp",
42103         "590",
42104         0
42105       ],
42106       [
42107         "Guam",
42108         "gu",
42109         "1671"
42110       ],
42111       [
42112         "Guatemala",
42113         "gt",
42114         "502"
42115       ],
42116       [
42117         "Guernsey",
42118         "gg",
42119         "44",
42120         1
42121       ],
42122       [
42123         "Guinea (Guinée)",
42124         "gn",
42125         "224"
42126       ],
42127       [
42128         "Guinea-Bissau (Guiné Bissau)",
42129         "gw",
42130         "245"
42131       ],
42132       [
42133         "Guyana",
42134         "gy",
42135         "592"
42136       ],
42137       [
42138         "Haiti",
42139         "ht",
42140         "509"
42141       ],
42142       [
42143         "Honduras",
42144         "hn",
42145         "504"
42146       ],
42147       [
42148         "Hong Kong (香港)",
42149         "hk",
42150         "852"
42151       ],
42152       [
42153         "Hungary (Magyarország)",
42154         "hu",
42155         "36"
42156       ],
42157       [
42158         "Iceland (Ísland)",
42159         "is",
42160         "354"
42161       ],
42162       [
42163         "India (भारत)",
42164         "in",
42165         "91"
42166       ],
42167       [
42168         "Indonesia",
42169         "id",
42170         "62"
42171       ],
42172       [
42173         "Iran (‫ایران‬‎)",
42174         "ir",
42175         "98"
42176       ],
42177       [
42178         "Iraq (‫العراق‬‎)",
42179         "iq",
42180         "964"
42181       ],
42182       [
42183         "Ireland",
42184         "ie",
42185         "353"
42186       ],
42187       [
42188         "Isle of Man",
42189         "im",
42190         "44",
42191         2
42192       ],
42193       [
42194         "Israel (‫ישראל‬‎)",
42195         "il",
42196         "972"
42197       ],
42198       [
42199         "Italy (Italia)",
42200         "it",
42201         "39",
42202         0
42203       ],
42204       [
42205         "Jamaica",
42206         "jm",
42207         "1876"
42208       ],
42209       [
42210         "Japan (日本)",
42211         "jp",
42212         "81"
42213       ],
42214       [
42215         "Jersey",
42216         "je",
42217         "44",
42218         3
42219       ],
42220       [
42221         "Jordan (‫الأردن‬‎)",
42222         "jo",
42223         "962"
42224       ],
42225       [
42226         "Kazakhstan (Казахстан)",
42227         "kz",
42228         "7",
42229         1
42230       ],
42231       [
42232         "Kenya",
42233         "ke",
42234         "254"
42235       ],
42236       [
42237         "Kiribati",
42238         "ki",
42239         "686"
42240       ],
42241       [
42242         "Kosovo",
42243         "xk",
42244         "383"
42245       ],
42246       [
42247         "Kuwait (‫الكويت‬‎)",
42248         "kw",
42249         "965"
42250       ],
42251       [
42252         "Kyrgyzstan (Кыргызстан)",
42253         "kg",
42254         "996"
42255       ],
42256       [
42257         "Laos (ລາວ)",
42258         "la",
42259         "856"
42260       ],
42261       [
42262         "Latvia (Latvija)",
42263         "lv",
42264         "371"
42265       ],
42266       [
42267         "Lebanon (‫لبنان‬‎)",
42268         "lb",
42269         "961"
42270       ],
42271       [
42272         "Lesotho",
42273         "ls",
42274         "266"
42275       ],
42276       [
42277         "Liberia",
42278         "lr",
42279         "231"
42280       ],
42281       [
42282         "Libya (‫ليبيا‬‎)",
42283         "ly",
42284         "218"
42285       ],
42286       [
42287         "Liechtenstein",
42288         "li",
42289         "423"
42290       ],
42291       [
42292         "Lithuania (Lietuva)",
42293         "lt",
42294         "370"
42295       ],
42296       [
42297         "Luxembourg",
42298         "lu",
42299         "352"
42300       ],
42301       [
42302         "Macau (澳門)",
42303         "mo",
42304         "853"
42305       ],
42306       [
42307         "Macedonia (FYROM) (Македонија)",
42308         "mk",
42309         "389"
42310       ],
42311       [
42312         "Madagascar (Madagasikara)",
42313         "mg",
42314         "261"
42315       ],
42316       [
42317         "Malawi",
42318         "mw",
42319         "265"
42320       ],
42321       [
42322         "Malaysia",
42323         "my",
42324         "60"
42325       ],
42326       [
42327         "Maldives",
42328         "mv",
42329         "960"
42330       ],
42331       [
42332         "Mali",
42333         "ml",
42334         "223"
42335       ],
42336       [
42337         "Malta",
42338         "mt",
42339         "356"
42340       ],
42341       [
42342         "Marshall Islands",
42343         "mh",
42344         "692"
42345       ],
42346       [
42347         "Martinique",
42348         "mq",
42349         "596"
42350       ],
42351       [
42352         "Mauritania (‫موريتانيا‬‎)",
42353         "mr",
42354         "222"
42355       ],
42356       [
42357         "Mauritius (Moris)",
42358         "mu",
42359         "230"
42360       ],
42361       [
42362         "Mayotte",
42363         "yt",
42364         "262",
42365         1
42366       ],
42367       [
42368         "Mexico (México)",
42369         "mx",
42370         "52"
42371       ],
42372       [
42373         "Micronesia",
42374         "fm",
42375         "691"
42376       ],
42377       [
42378         "Moldova (Republica Moldova)",
42379         "md",
42380         "373"
42381       ],
42382       [
42383         "Monaco",
42384         "mc",
42385         "377"
42386       ],
42387       [
42388         "Mongolia (Монгол)",
42389         "mn",
42390         "976"
42391       ],
42392       [
42393         "Montenegro (Crna Gora)",
42394         "me",
42395         "382"
42396       ],
42397       [
42398         "Montserrat",
42399         "ms",
42400         "1664"
42401       ],
42402       [
42403         "Morocco (‫المغرب‬‎)",
42404         "ma",
42405         "212",
42406         0
42407       ],
42408       [
42409         "Mozambique (Moçambique)",
42410         "mz",
42411         "258"
42412       ],
42413       [
42414         "Myanmar (Burma) (မြန်မာ)",
42415         "mm",
42416         "95"
42417       ],
42418       [
42419         "Namibia (Namibië)",
42420         "na",
42421         "264"
42422       ],
42423       [
42424         "Nauru",
42425         "nr",
42426         "674"
42427       ],
42428       [
42429         "Nepal (नेपाल)",
42430         "np",
42431         "977"
42432       ],
42433       [
42434         "Netherlands (Nederland)",
42435         "nl",
42436         "31"
42437       ],
42438       [
42439         "New Caledonia (Nouvelle-Calédonie)",
42440         "nc",
42441         "687"
42442       ],
42443       [
42444         "New Zealand",
42445         "nz",
42446         "64"
42447       ],
42448       [
42449         "Nicaragua",
42450         "ni",
42451         "505"
42452       ],
42453       [
42454         "Niger (Nijar)",
42455         "ne",
42456         "227"
42457       ],
42458       [
42459         "Nigeria",
42460         "ng",
42461         "234"
42462       ],
42463       [
42464         "Niue",
42465         "nu",
42466         "683"
42467       ],
42468       [
42469         "Norfolk Island",
42470         "nf",
42471         "672"
42472       ],
42473       [
42474         "North Korea (조선 민주주의 인민 공화국)",
42475         "kp",
42476         "850"
42477       ],
42478       [
42479         "Northern Mariana Islands",
42480         "mp",
42481         "1670"
42482       ],
42483       [
42484         "Norway (Norge)",
42485         "no",
42486         "47",
42487         0
42488       ],
42489       [
42490         "Oman (‫عُمان‬‎)",
42491         "om",
42492         "968"
42493       ],
42494       [
42495         "Pakistan (‫پاکستان‬‎)",
42496         "pk",
42497         "92"
42498       ],
42499       [
42500         "Palau",
42501         "pw",
42502         "680"
42503       ],
42504       [
42505         "Palestine (‫فلسطين‬‎)",
42506         "ps",
42507         "970"
42508       ],
42509       [
42510         "Panama (Panamá)",
42511         "pa",
42512         "507"
42513       ],
42514       [
42515         "Papua New Guinea",
42516         "pg",
42517         "675"
42518       ],
42519       [
42520         "Paraguay",
42521         "py",
42522         "595"
42523       ],
42524       [
42525         "Peru (Perú)",
42526         "pe",
42527         "51"
42528       ],
42529       [
42530         "Philippines",
42531         "ph",
42532         "63"
42533       ],
42534       [
42535         "Poland (Polska)",
42536         "pl",
42537         "48"
42538       ],
42539       [
42540         "Portugal",
42541         "pt",
42542         "351"
42543       ],
42544       [
42545         "Puerto Rico",
42546         "pr",
42547         "1",
42548         3,
42549         ["787", "939"]
42550       ],
42551       [
42552         "Qatar (‫قطر‬‎)",
42553         "qa",
42554         "974"
42555       ],
42556       [
42557         "Réunion (La Réunion)",
42558         "re",
42559         "262",
42560         0
42561       ],
42562       [
42563         "Romania (România)",
42564         "ro",
42565         "40"
42566       ],
42567       [
42568         "Russia (Россия)",
42569         "ru",
42570         "7",
42571         0
42572       ],
42573       [
42574         "Rwanda",
42575         "rw",
42576         "250"
42577       ],
42578       [
42579         "Saint Barthélemy",
42580         "bl",
42581         "590",
42582         1
42583       ],
42584       [
42585         "Saint Helena",
42586         "sh",
42587         "290"
42588       ],
42589       [
42590         "Saint Kitts and Nevis",
42591         "kn",
42592         "1869"
42593       ],
42594       [
42595         "Saint Lucia",
42596         "lc",
42597         "1758"
42598       ],
42599       [
42600         "Saint Martin (Saint-Martin (partie française))",
42601         "mf",
42602         "590",
42603         2
42604       ],
42605       [
42606         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42607         "pm",
42608         "508"
42609       ],
42610       [
42611         "Saint Vincent and the Grenadines",
42612         "vc",
42613         "1784"
42614       ],
42615       [
42616         "Samoa",
42617         "ws",
42618         "685"
42619       ],
42620       [
42621         "San Marino",
42622         "sm",
42623         "378"
42624       ],
42625       [
42626         "São Tomé and Príncipe (São Tomé e Príncipe)",
42627         "st",
42628         "239"
42629       ],
42630       [
42631         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42632         "sa",
42633         "966"
42634       ],
42635       [
42636         "Senegal (Sénégal)",
42637         "sn",
42638         "221"
42639       ],
42640       [
42641         "Serbia (Србија)",
42642         "rs",
42643         "381"
42644       ],
42645       [
42646         "Seychelles",
42647         "sc",
42648         "248"
42649       ],
42650       [
42651         "Sierra Leone",
42652         "sl",
42653         "232"
42654       ],
42655       [
42656         "Singapore",
42657         "sg",
42658         "65"
42659       ],
42660       [
42661         "Sint Maarten",
42662         "sx",
42663         "1721"
42664       ],
42665       [
42666         "Slovakia (Slovensko)",
42667         "sk",
42668         "421"
42669       ],
42670       [
42671         "Slovenia (Slovenija)",
42672         "si",
42673         "386"
42674       ],
42675       [
42676         "Solomon Islands",
42677         "sb",
42678         "677"
42679       ],
42680       [
42681         "Somalia (Soomaaliya)",
42682         "so",
42683         "252"
42684       ],
42685       [
42686         "South Africa",
42687         "za",
42688         "27"
42689       ],
42690       [
42691         "South Korea (대한민국)",
42692         "kr",
42693         "82"
42694       ],
42695       [
42696         "South Sudan (‫جنوب السودان‬‎)",
42697         "ss",
42698         "211"
42699       ],
42700       [
42701         "Spain (España)",
42702         "es",
42703         "34"
42704       ],
42705       [
42706         "Sri Lanka (ශ්‍රී ලංකාව)",
42707         "lk",
42708         "94"
42709       ],
42710       [
42711         "Sudan (‫السودان‬‎)",
42712         "sd",
42713         "249"
42714       ],
42715       [
42716         "Suriname",
42717         "sr",
42718         "597"
42719       ],
42720       [
42721         "Svalbard and Jan Mayen",
42722         "sj",
42723         "47",
42724         1
42725       ],
42726       [
42727         "Swaziland",
42728         "sz",
42729         "268"
42730       ],
42731       [
42732         "Sweden (Sverige)",
42733         "se",
42734         "46"
42735       ],
42736       [
42737         "Switzerland (Schweiz)",
42738         "ch",
42739         "41"
42740       ],
42741       [
42742         "Syria (‫سوريا‬‎)",
42743         "sy",
42744         "963"
42745       ],
42746       [
42747         "Taiwan (台灣)",
42748         "tw",
42749         "886"
42750       ],
42751       [
42752         "Tajikistan",
42753         "tj",
42754         "992"
42755       ],
42756       [
42757         "Tanzania",
42758         "tz",
42759         "255"
42760       ],
42761       [
42762         "Thailand (ไทย)",
42763         "th",
42764         "66"
42765       ],
42766       [
42767         "Timor-Leste",
42768         "tl",
42769         "670"
42770       ],
42771       [
42772         "Togo",
42773         "tg",
42774         "228"
42775       ],
42776       [
42777         "Tokelau",
42778         "tk",
42779         "690"
42780       ],
42781       [
42782         "Tonga",
42783         "to",
42784         "676"
42785       ],
42786       [
42787         "Trinidad and Tobago",
42788         "tt",
42789         "1868"
42790       ],
42791       [
42792         "Tunisia (‫تونس‬‎)",
42793         "tn",
42794         "216"
42795       ],
42796       [
42797         "Turkey (Türkiye)",
42798         "tr",
42799         "90"
42800       ],
42801       [
42802         "Turkmenistan",
42803         "tm",
42804         "993"
42805       ],
42806       [
42807         "Turks and Caicos Islands",
42808         "tc",
42809         "1649"
42810       ],
42811       [
42812         "Tuvalu",
42813         "tv",
42814         "688"
42815       ],
42816       [
42817         "U.S. Virgin Islands",
42818         "vi",
42819         "1340"
42820       ],
42821       [
42822         "Uganda",
42823         "ug",
42824         "256"
42825       ],
42826       [
42827         "Ukraine (Україна)",
42828         "ua",
42829         "380"
42830       ],
42831       [
42832         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42833         "ae",
42834         "971"
42835       ],
42836       [
42837         "United Kingdom",
42838         "gb",
42839         "44",
42840         0
42841       ],
42842       [
42843         "United States",
42844         "us",
42845         "1",
42846         0
42847       ],
42848       [
42849         "Uruguay",
42850         "uy",
42851         "598"
42852       ],
42853       [
42854         "Uzbekistan (Oʻzbekiston)",
42855         "uz",
42856         "998"
42857       ],
42858       [
42859         "Vanuatu",
42860         "vu",
42861         "678"
42862       ],
42863       [
42864         "Vatican City (Città del Vaticano)",
42865         "va",
42866         "39",
42867         1
42868       ],
42869       [
42870         "Venezuela",
42871         "ve",
42872         "58"
42873       ],
42874       [
42875         "Vietnam (Việt Nam)",
42876         "vn",
42877         "84"
42878       ],
42879       [
42880         "Wallis and Futuna (Wallis-et-Futuna)",
42881         "wf",
42882         "681"
42883       ],
42884       [
42885         "Western Sahara (‫الصحراء الغربية‬‎)",
42886         "eh",
42887         "212",
42888         1
42889       ],
42890       [
42891         "Yemen (‫اليمن‬‎)",
42892         "ye",
42893         "967"
42894       ],
42895       [
42896         "Zambia",
42897         "zm",
42898         "260"
42899       ],
42900       [
42901         "Zimbabwe",
42902         "zw",
42903         "263"
42904       ],
42905       [
42906         "Åland Islands",
42907         "ax",
42908         "358",
42909         1
42910       ]
42911   ];
42912   
42913   return d;
42914 }/**
42915 *    This script refer to:
42916 *    Title: International Telephone Input
42917 *    Author: Jack O'Connor
42918 *    Code version:  v12.1.12
42919 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42920 **/
42921
42922 /**
42923  * @class Roo.bootstrap.PhoneInput
42924  * @extends Roo.bootstrap.TriggerField
42925  * An input with International dial-code selection
42926  
42927  * @cfg {String} defaultDialCode default '+852'
42928  * @cfg {Array} preferedCountries default []
42929   
42930  * @constructor
42931  * Create a new PhoneInput.
42932  * @param {Object} config Configuration options
42933  */
42934
42935 Roo.bootstrap.PhoneInput = function(config) {
42936     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42937 };
42938
42939 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42940         
42941         listWidth: undefined,
42942         
42943         selectedClass: 'active',
42944         
42945         invalidClass : "has-warning",
42946         
42947         validClass: 'has-success',
42948         
42949         allowed: '0123456789',
42950         
42951         max_length: 15,
42952         
42953         /**
42954          * @cfg {String} defaultDialCode The default dial code when initializing the input
42955          */
42956         defaultDialCode: '+852',
42957         
42958         /**
42959          * @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
42960          */
42961         preferedCountries: false,
42962         
42963         getAutoCreate : function()
42964         {
42965             var data = Roo.bootstrap.PhoneInputData();
42966             var align = this.labelAlign || this.parentLabelAlign();
42967             var id = Roo.id();
42968             
42969             this.allCountries = [];
42970             this.dialCodeMapping = [];
42971             
42972             for (var i = 0; i < data.length; i++) {
42973               var c = data[i];
42974               this.allCountries[i] = {
42975                 name: c[0],
42976                 iso2: c[1],
42977                 dialCode: c[2],
42978                 priority: c[3] || 0,
42979                 areaCodes: c[4] || null
42980               };
42981               this.dialCodeMapping[c[2]] = {
42982                   name: c[0],
42983                   iso2: c[1],
42984                   priority: c[3] || 0,
42985                   areaCodes: c[4] || null
42986               };
42987             }
42988             
42989             var cfg = {
42990                 cls: 'form-group',
42991                 cn: []
42992             };
42993             
42994             var input =  {
42995                 tag: 'input',
42996                 id : id,
42997                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42998                 maxlength: this.max_length,
42999                 cls : 'form-control tel-input',
43000                 autocomplete: 'new-password'
43001             };
43002             
43003             var hiddenInput = {
43004                 tag: 'input',
43005                 type: 'hidden',
43006                 cls: 'hidden-tel-input'
43007             };
43008             
43009             if (this.name) {
43010                 hiddenInput.name = this.name;
43011             }
43012             
43013             if (this.disabled) {
43014                 input.disabled = true;
43015             }
43016             
43017             var flag_container = {
43018                 tag: 'div',
43019                 cls: 'flag-box',
43020                 cn: [
43021                     {
43022                         tag: 'div',
43023                         cls: 'flag'
43024                     },
43025                     {
43026                         tag: 'div',
43027                         cls: 'caret'
43028                     }
43029                 ]
43030             };
43031             
43032             var box = {
43033                 tag: 'div',
43034                 cls: this.hasFeedback ? 'has-feedback' : '',
43035                 cn: [
43036                     hiddenInput,
43037                     input,
43038                     {
43039                         tag: 'input',
43040                         cls: 'dial-code-holder',
43041                         disabled: true
43042                     }
43043                 ]
43044             };
43045             
43046             var container = {
43047                 cls: 'roo-select2-container input-group',
43048                 cn: [
43049                     flag_container,
43050                     box
43051                 ]
43052             };
43053             
43054             if (this.fieldLabel.length) {
43055                 var indicator = {
43056                     tag: 'i',
43057                     tooltip: 'This field is required'
43058                 };
43059                 
43060                 var label = {
43061                     tag: 'label',
43062                     'for':  id,
43063                     cls: 'control-label',
43064                     cn: []
43065                 };
43066                 
43067                 var label_text = {
43068                     tag: 'span',
43069                     html: this.fieldLabel
43070                 };
43071                 
43072                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43073                 label.cn = [
43074                     indicator,
43075                     label_text
43076                 ];
43077                 
43078                 if(this.indicatorpos == 'right') {
43079                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43080                     label.cn = [
43081                         label_text,
43082                         indicator
43083                     ];
43084                 }
43085                 
43086                 if(align == 'left') {
43087                     container = {
43088                         tag: 'div',
43089                         cn: [
43090                             container
43091                         ]
43092                     };
43093                     
43094                     if(this.labelWidth > 12){
43095                         label.style = "width: " + this.labelWidth + 'px';
43096                     }
43097                     if(this.labelWidth < 13 && this.labelmd == 0){
43098                         this.labelmd = this.labelWidth;
43099                     }
43100                     if(this.labellg > 0){
43101                         label.cls += ' col-lg-' + this.labellg;
43102                         input.cls += ' col-lg-' + (12 - this.labellg);
43103                     }
43104                     if(this.labelmd > 0){
43105                         label.cls += ' col-md-' + this.labelmd;
43106                         container.cls += ' col-md-' + (12 - this.labelmd);
43107                     }
43108                     if(this.labelsm > 0){
43109                         label.cls += ' col-sm-' + this.labelsm;
43110                         container.cls += ' col-sm-' + (12 - this.labelsm);
43111                     }
43112                     if(this.labelxs > 0){
43113                         label.cls += ' col-xs-' + this.labelxs;
43114                         container.cls += ' col-xs-' + (12 - this.labelxs);
43115                     }
43116                 }
43117             }
43118             
43119             cfg.cn = [
43120                 label,
43121                 container
43122             ];
43123             
43124             var settings = this;
43125             
43126             ['xs','sm','md','lg'].map(function(size){
43127                 if (settings[size]) {
43128                     cfg.cls += ' col-' + size + '-' + settings[size];
43129                 }
43130             });
43131             
43132             this.store = new Roo.data.Store({
43133                 proxy : new Roo.data.MemoryProxy({}),
43134                 reader : new Roo.data.JsonReader({
43135                     fields : [
43136                         {
43137                             'name' : 'name',
43138                             'type' : 'string'
43139                         },
43140                         {
43141                             'name' : 'iso2',
43142                             'type' : 'string'
43143                         },
43144                         {
43145                             'name' : 'dialCode',
43146                             'type' : 'string'
43147                         },
43148                         {
43149                             'name' : 'priority',
43150                             'type' : 'string'
43151                         },
43152                         {
43153                             'name' : 'areaCodes',
43154                             'type' : 'string'
43155                         }
43156                     ]
43157                 })
43158             });
43159             
43160             if(!this.preferedCountries) {
43161                 this.preferedCountries = [
43162                     'hk',
43163                     'gb',
43164                     'us'
43165                 ];
43166             }
43167             
43168             var p = this.preferedCountries.reverse();
43169             
43170             if(p) {
43171                 for (var i = 0; i < p.length; i++) {
43172                     for (var j = 0; j < this.allCountries.length; j++) {
43173                         if(this.allCountries[j].iso2 == p[i]) {
43174                             var t = this.allCountries[j];
43175                             this.allCountries.splice(j,1);
43176                             this.allCountries.unshift(t);
43177                         }
43178                     } 
43179                 }
43180             }
43181             
43182             this.store.proxy.data = {
43183                 success: true,
43184                 data: this.allCountries
43185             };
43186             
43187             return cfg;
43188         },
43189         
43190         initEvents : function()
43191         {
43192             this.createList();
43193             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43194             
43195             this.indicator = this.indicatorEl();
43196             this.flag = this.flagEl();
43197             this.dialCodeHolder = this.dialCodeHolderEl();
43198             
43199             this.trigger = this.el.select('div.flag-box',true).first();
43200             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43201             
43202             var _this = this;
43203             
43204             (function(){
43205                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43206                 _this.list.setWidth(lw);
43207             }).defer(100);
43208             
43209             this.list.on('mouseover', this.onViewOver, this);
43210             this.list.on('mousemove', this.onViewMove, this);
43211             this.inputEl().on("keyup", this.onKeyUp, this);
43212             this.inputEl().on("keypress", this.onKeyPress, this);
43213             
43214             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43215
43216             this.view = new Roo.View(this.list, this.tpl, {
43217                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43218             });
43219             
43220             this.view.on('click', this.onViewClick, this);
43221             this.setValue(this.defaultDialCode);
43222         },
43223         
43224         onTriggerClick : function(e)
43225         {
43226             Roo.log('trigger click');
43227             if(this.disabled){
43228                 return;
43229             }
43230             
43231             if(this.isExpanded()){
43232                 this.collapse();
43233                 this.hasFocus = false;
43234             }else {
43235                 this.store.load({});
43236                 this.hasFocus = true;
43237                 this.expand();
43238             }
43239         },
43240         
43241         isExpanded : function()
43242         {
43243             return this.list.isVisible();
43244         },
43245         
43246         collapse : function()
43247         {
43248             if(!this.isExpanded()){
43249                 return;
43250             }
43251             this.list.hide();
43252             Roo.get(document).un('mousedown', this.collapseIf, this);
43253             Roo.get(document).un('mousewheel', this.collapseIf, this);
43254             this.fireEvent('collapse', this);
43255             this.validate();
43256         },
43257         
43258         expand : function()
43259         {
43260             Roo.log('expand');
43261
43262             if(this.isExpanded() || !this.hasFocus){
43263                 return;
43264             }
43265             
43266             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43267             this.list.setWidth(lw);
43268             
43269             this.list.show();
43270             this.restrictHeight();
43271             
43272             Roo.get(document).on('mousedown', this.collapseIf, this);
43273             Roo.get(document).on('mousewheel', this.collapseIf, this);
43274             
43275             this.fireEvent('expand', this);
43276         },
43277         
43278         restrictHeight : function()
43279         {
43280             this.list.alignTo(this.inputEl(), this.listAlign);
43281             this.list.alignTo(this.inputEl(), this.listAlign);
43282         },
43283         
43284         onViewOver : function(e, t)
43285         {
43286             if(this.inKeyMode){
43287                 return;
43288             }
43289             var item = this.view.findItemFromChild(t);
43290             
43291             if(item){
43292                 var index = this.view.indexOf(item);
43293                 this.select(index, false);
43294             }
43295         },
43296
43297         // private
43298         onViewClick : function(view, doFocus, el, e)
43299         {
43300             var index = this.view.getSelectedIndexes()[0];
43301             
43302             var r = this.store.getAt(index);
43303             
43304             if(r){
43305                 this.onSelect(r, index);
43306             }
43307             if(doFocus !== false && !this.blockFocus){
43308                 this.inputEl().focus();
43309             }
43310         },
43311         
43312         onViewMove : function(e, t)
43313         {
43314             this.inKeyMode = false;
43315         },
43316         
43317         select : function(index, scrollIntoView)
43318         {
43319             this.selectedIndex = index;
43320             this.view.select(index);
43321             if(scrollIntoView !== false){
43322                 var el = this.view.getNode(index);
43323                 if(el){
43324                     this.list.scrollChildIntoView(el, false);
43325                 }
43326             }
43327         },
43328         
43329         createList : function()
43330         {
43331             this.list = Roo.get(document.body).createChild({
43332                 tag: 'ul',
43333                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43334                 style: 'display:none'
43335             });
43336             
43337             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43338         },
43339         
43340         collapseIf : function(e)
43341         {
43342             var in_combo  = e.within(this.el);
43343             var in_list =  e.within(this.list);
43344             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43345             
43346             if (in_combo || in_list || is_list) {
43347                 return;
43348             }
43349             this.collapse();
43350         },
43351         
43352         onSelect : function(record, index)
43353         {
43354             if(this.fireEvent('beforeselect', this, record, index) !== false){
43355                 
43356                 this.setFlagClass(record.data.iso2);
43357                 this.setDialCode(record.data.dialCode);
43358                 this.hasFocus = false;
43359                 this.collapse();
43360                 this.fireEvent('select', this, record, index);
43361             }
43362         },
43363         
43364         flagEl : function()
43365         {
43366             var flag = this.el.select('div.flag',true).first();
43367             if(!flag){
43368                 return false;
43369             }
43370             return flag;
43371         },
43372         
43373         dialCodeHolderEl : function()
43374         {
43375             var d = this.el.select('input.dial-code-holder',true).first();
43376             if(!d){
43377                 return false;
43378             }
43379             return d;
43380         },
43381         
43382         setDialCode : function(v)
43383         {
43384             this.dialCodeHolder.dom.value = '+'+v;
43385         },
43386         
43387         setFlagClass : function(n)
43388         {
43389             this.flag.dom.className = 'flag '+n;
43390         },
43391         
43392         getValue : function()
43393         {
43394             var v = this.inputEl().getValue();
43395             if(this.dialCodeHolder) {
43396                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43397             }
43398             return v;
43399         },
43400         
43401         setValue : function(v)
43402         {
43403             var d = this.getDialCode(v);
43404             
43405             //invalid dial code
43406             if(v.length == 0 || !d || d.length == 0) {
43407                 if(this.rendered){
43408                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43409                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43410                 }
43411                 return;
43412             }
43413             
43414             //valid dial code
43415             this.setFlagClass(this.dialCodeMapping[d].iso2);
43416             this.setDialCode(d);
43417             this.inputEl().dom.value = v.replace('+'+d,'');
43418             this.hiddenEl().dom.value = this.getValue();
43419             
43420             this.validate();
43421         },
43422         
43423         getDialCode : function(v)
43424         {
43425             v = v ||  '';
43426             
43427             if (v.length == 0) {
43428                 return this.dialCodeHolder.dom.value;
43429             }
43430             
43431             var dialCode = "";
43432             if (v.charAt(0) != "+") {
43433                 return false;
43434             }
43435             var numericChars = "";
43436             for (var i = 1; i < v.length; i++) {
43437               var c = v.charAt(i);
43438               if (!isNaN(c)) {
43439                 numericChars += c;
43440                 if (this.dialCodeMapping[numericChars]) {
43441                   dialCode = v.substr(1, i);
43442                 }
43443                 if (numericChars.length == 4) {
43444                   break;
43445                 }
43446               }
43447             }
43448             return dialCode;
43449         },
43450         
43451         reset : function()
43452         {
43453             this.setValue(this.defaultDialCode);
43454             this.validate();
43455         },
43456         
43457         hiddenEl : function()
43458         {
43459             return this.el.select('input.hidden-tel-input',true).first();
43460         },
43461         
43462         // after setting val
43463         onKeyUp : function(e){
43464             this.setValue(this.getValue());
43465         },
43466         
43467         onKeyPress : function(e){
43468             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43469                 e.stopEvent();
43470             }
43471         }
43472         
43473 });
43474 /**
43475  * @class Roo.bootstrap.MoneyField
43476  * @extends Roo.bootstrap.ComboBox
43477  * Bootstrap MoneyField class
43478  * 
43479  * @constructor
43480  * Create a new MoneyField.
43481  * @param {Object} config Configuration options
43482  */
43483
43484 Roo.bootstrap.MoneyField = function(config) {
43485     
43486     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43487     
43488 };
43489
43490 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43491     
43492     /**
43493      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43494      */
43495     allowDecimals : true,
43496     /**
43497      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43498      */
43499     decimalSeparator : ".",
43500     /**
43501      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43502      */
43503     decimalPrecision : 0,
43504     /**
43505      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43506      */
43507     allowNegative : true,
43508     /**
43509      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43510      */
43511     allowZero: true,
43512     /**
43513      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43514      */
43515     minValue : Number.NEGATIVE_INFINITY,
43516     /**
43517      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43518      */
43519     maxValue : Number.MAX_VALUE,
43520     /**
43521      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43522      */
43523     minText : "The minimum value for this field is {0}",
43524     /**
43525      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43526      */
43527     maxText : "The maximum value for this field is {0}",
43528     /**
43529      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43530      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43531      */
43532     nanText : "{0} is not a valid number",
43533     /**
43534      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43535      */
43536     castInt : true,
43537     /**
43538      * @cfg {String} defaults currency of the MoneyField
43539      * value should be in lkey
43540      */
43541     defaultCurrency : false,
43542     /**
43543      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43544      */
43545     thousandsDelimiter : false,
43546     /**
43547      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43548      */
43549     max_length: false,
43550     
43551     inputlg : 9,
43552     inputmd : 9,
43553     inputsm : 9,
43554     inputxs : 6,
43555     
43556     store : false,
43557     
43558     getAutoCreate : function()
43559     {
43560         var align = this.labelAlign || this.parentLabelAlign();
43561         
43562         var id = Roo.id();
43563
43564         var cfg = {
43565             cls: 'form-group',
43566             cn: []
43567         };
43568
43569         var input =  {
43570             tag: 'input',
43571             id : id,
43572             cls : 'form-control roo-money-amount-input',
43573             autocomplete: 'new-password'
43574         };
43575         
43576         var hiddenInput = {
43577             tag: 'input',
43578             type: 'hidden',
43579             id: Roo.id(),
43580             cls: 'hidden-number-input'
43581         };
43582         
43583         if(this.max_length) {
43584             input.maxlength = this.max_length; 
43585         }
43586         
43587         if (this.name) {
43588             hiddenInput.name = this.name;
43589         }
43590
43591         if (this.disabled) {
43592             input.disabled = true;
43593         }
43594
43595         var clg = 12 - this.inputlg;
43596         var cmd = 12 - this.inputmd;
43597         var csm = 12 - this.inputsm;
43598         var cxs = 12 - this.inputxs;
43599         
43600         var container = {
43601             tag : 'div',
43602             cls : 'row roo-money-field',
43603             cn : [
43604                 {
43605                     tag : 'div',
43606                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43607                     cn : [
43608                         {
43609                             tag : 'div',
43610                             cls: 'roo-select2-container input-group',
43611                             cn: [
43612                                 {
43613                                     tag : 'input',
43614                                     cls : 'form-control roo-money-currency-input',
43615                                     autocomplete: 'new-password',
43616                                     readOnly : 1,
43617                                     name : this.currencyName
43618                                 },
43619                                 {
43620                                     tag :'span',
43621                                     cls : 'input-group-addon',
43622                                     cn : [
43623                                         {
43624                                             tag: 'span',
43625                                             cls: 'caret'
43626                                         }
43627                                     ]
43628                                 }
43629                             ]
43630                         }
43631                     ]
43632                 },
43633                 {
43634                     tag : 'div',
43635                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43636                     cn : [
43637                         {
43638                             tag: 'div',
43639                             cls: this.hasFeedback ? 'has-feedback' : '',
43640                             cn: [
43641                                 input
43642                             ]
43643                         }
43644                     ]
43645                 }
43646             ]
43647             
43648         };
43649         
43650         if (this.fieldLabel.length) {
43651             var indicator = {
43652                 tag: 'i',
43653                 tooltip: 'This field is required'
43654             };
43655
43656             var label = {
43657                 tag: 'label',
43658                 'for':  id,
43659                 cls: 'control-label',
43660                 cn: []
43661             };
43662
43663             var label_text = {
43664                 tag: 'span',
43665                 html: this.fieldLabel
43666             };
43667
43668             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43669             label.cn = [
43670                 indicator,
43671                 label_text
43672             ];
43673
43674             if(this.indicatorpos == 'right') {
43675                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43676                 label.cn = [
43677                     label_text,
43678                     indicator
43679                 ];
43680             }
43681
43682             if(align == 'left') {
43683                 container = {
43684                     tag: 'div',
43685                     cn: [
43686                         container
43687                     ]
43688                 };
43689
43690                 if(this.labelWidth > 12){
43691                     label.style = "width: " + this.labelWidth + 'px';
43692                 }
43693                 if(this.labelWidth < 13 && this.labelmd == 0){
43694                     this.labelmd = this.labelWidth;
43695                 }
43696                 if(this.labellg > 0){
43697                     label.cls += ' col-lg-' + this.labellg;
43698                     input.cls += ' col-lg-' + (12 - this.labellg);
43699                 }
43700                 if(this.labelmd > 0){
43701                     label.cls += ' col-md-' + this.labelmd;
43702                     container.cls += ' col-md-' + (12 - this.labelmd);
43703                 }
43704                 if(this.labelsm > 0){
43705                     label.cls += ' col-sm-' + this.labelsm;
43706                     container.cls += ' col-sm-' + (12 - this.labelsm);
43707                 }
43708                 if(this.labelxs > 0){
43709                     label.cls += ' col-xs-' + this.labelxs;
43710                     container.cls += ' col-xs-' + (12 - this.labelxs);
43711                 }
43712             }
43713         }
43714
43715         cfg.cn = [
43716             label,
43717             container,
43718             hiddenInput
43719         ];
43720         
43721         var settings = this;
43722
43723         ['xs','sm','md','lg'].map(function(size){
43724             if (settings[size]) {
43725                 cfg.cls += ' col-' + size + '-' + settings[size];
43726             }
43727         });
43728         
43729         return cfg;
43730     },
43731     
43732     initEvents : function()
43733     {
43734         this.indicator = this.indicatorEl();
43735         
43736         this.initCurrencyEvent();
43737         
43738         this.initNumberEvent();
43739     },
43740     
43741     initCurrencyEvent : function()
43742     {
43743         if (!this.store) {
43744             throw "can not find store for combo";
43745         }
43746         
43747         this.store = Roo.factory(this.store, Roo.data);
43748         this.store.parent = this;
43749         
43750         this.createList();
43751         
43752         this.triggerEl = this.el.select('.input-group-addon', true).first();
43753         
43754         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43755         
43756         var _this = this;
43757         
43758         (function(){
43759             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43760             _this.list.setWidth(lw);
43761         }).defer(100);
43762         
43763         this.list.on('mouseover', this.onViewOver, this);
43764         this.list.on('mousemove', this.onViewMove, this);
43765         this.list.on('scroll', this.onViewScroll, this);
43766         
43767         if(!this.tpl){
43768             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43769         }
43770         
43771         this.view = new Roo.View(this.list, this.tpl, {
43772             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43773         });
43774         
43775         this.view.on('click', this.onViewClick, this);
43776         
43777         this.store.on('beforeload', this.onBeforeLoad, this);
43778         this.store.on('load', this.onLoad, this);
43779         this.store.on('loadexception', this.onLoadException, this);
43780         
43781         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43782             "up" : function(e){
43783                 this.inKeyMode = true;
43784                 this.selectPrev();
43785             },
43786
43787             "down" : function(e){
43788                 if(!this.isExpanded()){
43789                     this.onTriggerClick();
43790                 }else{
43791                     this.inKeyMode = true;
43792                     this.selectNext();
43793                 }
43794             },
43795
43796             "enter" : function(e){
43797                 this.collapse();
43798                 
43799                 if(this.fireEvent("specialkey", this, e)){
43800                     this.onViewClick(false);
43801                 }
43802                 
43803                 return true;
43804             },
43805
43806             "esc" : function(e){
43807                 this.collapse();
43808             },
43809
43810             "tab" : function(e){
43811                 this.collapse();
43812                 
43813                 if(this.fireEvent("specialkey", this, e)){
43814                     this.onViewClick(false);
43815                 }
43816                 
43817                 return true;
43818             },
43819
43820             scope : this,
43821
43822             doRelay : function(foo, bar, hname){
43823                 if(hname == 'down' || this.scope.isExpanded()){
43824                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43825                 }
43826                 return true;
43827             },
43828
43829             forceKeyDown: true
43830         });
43831         
43832         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43833         
43834     },
43835     
43836     initNumberEvent : function(e)
43837     {
43838         this.inputEl().on("keydown" , this.fireKey,  this);
43839         this.inputEl().on("focus", this.onFocus,  this);
43840         this.inputEl().on("blur", this.onBlur,  this);
43841         
43842         this.inputEl().relayEvent('keyup', this);
43843         
43844         if(this.indicator){
43845             this.indicator.addClass('invisible');
43846         }
43847  
43848         this.originalValue = this.getValue();
43849         
43850         if(this.validationEvent == 'keyup'){
43851             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43852             this.inputEl().on('keyup', this.filterValidation, this);
43853         }
43854         else if(this.validationEvent !== false){
43855             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43856         }
43857         
43858         if(this.selectOnFocus){
43859             this.on("focus", this.preFocus, this);
43860             
43861         }
43862         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43863             this.inputEl().on("keypress", this.filterKeys, this);
43864         } else {
43865             this.inputEl().relayEvent('keypress', this);
43866         }
43867         
43868         var allowed = "0123456789";
43869         
43870         if(this.allowDecimals){
43871             allowed += this.decimalSeparator;
43872         }
43873         
43874         if(this.allowNegative){
43875             allowed += "-";
43876         }
43877         
43878         if(this.thousandsDelimiter) {
43879             allowed += ",";
43880         }
43881         
43882         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43883         
43884         var keyPress = function(e){
43885             
43886             var k = e.getKey();
43887             
43888             var c = e.getCharCode();
43889             
43890             if(
43891                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43892                     allowed.indexOf(String.fromCharCode(c)) === -1
43893             ){
43894                 e.stopEvent();
43895                 return;
43896             }
43897             
43898             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43899                 return;
43900             }
43901             
43902             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43903                 e.stopEvent();
43904             }
43905         };
43906         
43907         this.inputEl().on("keypress", keyPress, this);
43908         
43909     },
43910     
43911     onTriggerClick : function(e)
43912     {   
43913         if(this.disabled){
43914             return;
43915         }
43916         
43917         this.page = 0;
43918         this.loadNext = false;
43919         
43920         if(this.isExpanded()){
43921             this.collapse();
43922             return;
43923         }
43924         
43925         this.hasFocus = true;
43926         
43927         if(this.triggerAction == 'all') {
43928             this.doQuery(this.allQuery, true);
43929             return;
43930         }
43931         
43932         this.doQuery(this.getRawValue());
43933     },
43934     
43935     getCurrency : function()
43936     {   
43937         var v = this.currencyEl().getValue();
43938         
43939         return v;
43940     },
43941     
43942     restrictHeight : function()
43943     {
43944         this.list.alignTo(this.currencyEl(), this.listAlign);
43945         this.list.alignTo(this.currencyEl(), this.listAlign);
43946     },
43947     
43948     onViewClick : function(view, doFocus, el, e)
43949     {
43950         var index = this.view.getSelectedIndexes()[0];
43951         
43952         var r = this.store.getAt(index);
43953         
43954         if(r){
43955             this.onSelect(r, index);
43956         }
43957     },
43958     
43959     onSelect : function(record, index){
43960         
43961         if(this.fireEvent('beforeselect', this, record, index) !== false){
43962         
43963             this.setFromCurrencyData(index > -1 ? record.data : false);
43964             
43965             this.collapse();
43966             
43967             this.fireEvent('select', this, record, index);
43968         }
43969     },
43970     
43971     setFromCurrencyData : function(o)
43972     {
43973         var currency = '';
43974         
43975         this.lastCurrency = o;
43976         
43977         if (this.currencyField) {
43978             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43979         } else {
43980             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43981         }
43982         
43983         this.lastSelectionText = currency;
43984         
43985         //setting default currency
43986         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43987             this.setCurrency(this.defaultCurrency);
43988             return;
43989         }
43990         
43991         this.setCurrency(currency);
43992     },
43993     
43994     setFromData : function(o)
43995     {
43996         var c = {};
43997         
43998         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43999         
44000         this.setFromCurrencyData(c);
44001         
44002         var value = '';
44003         
44004         if (this.name) {
44005             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44006         } else {
44007             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44008         }
44009         
44010         this.setValue(value);
44011         
44012     },
44013     
44014     setCurrency : function(v)
44015     {   
44016         this.currencyValue = v;
44017         
44018         if(this.rendered){
44019             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44020             this.validate();
44021         }
44022     },
44023     
44024     setValue : function(v)
44025     {
44026         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44027         
44028         this.value = v;
44029         
44030         if(this.rendered){
44031             
44032             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44033             
44034             this.inputEl().dom.value = (v == '') ? '' :
44035                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44036             
44037             if(!this.allowZero && v === '0') {
44038                 this.hiddenEl().dom.value = '';
44039                 this.inputEl().dom.value = '';
44040             }
44041             
44042             this.validate();
44043         }
44044     },
44045     
44046     getRawValue : function()
44047     {
44048         var v = this.inputEl().getValue();
44049         
44050         return v;
44051     },
44052     
44053     getValue : function()
44054     {
44055         return this.fixPrecision(this.parseValue(this.getRawValue()));
44056     },
44057     
44058     parseValue : function(value)
44059     {
44060         if(this.thousandsDelimiter) {
44061             value += "";
44062             r = new RegExp(",", "g");
44063             value = value.replace(r, "");
44064         }
44065         
44066         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44067         return isNaN(value) ? '' : value;
44068         
44069     },
44070     
44071     fixPrecision : function(value)
44072     {
44073         if(this.thousandsDelimiter) {
44074             value += "";
44075             r = new RegExp(",", "g");
44076             value = value.replace(r, "");
44077         }
44078         
44079         var nan = isNaN(value);
44080         
44081         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44082             return nan ? '' : value;
44083         }
44084         return parseFloat(value).toFixed(this.decimalPrecision);
44085     },
44086     
44087     decimalPrecisionFcn : function(v)
44088     {
44089         return Math.floor(v);
44090     },
44091     
44092     validateValue : function(value)
44093     {
44094         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44095             return false;
44096         }
44097         
44098         var num = this.parseValue(value);
44099         
44100         if(isNaN(num)){
44101             this.markInvalid(String.format(this.nanText, value));
44102             return false;
44103         }
44104         
44105         if(num < this.minValue){
44106             this.markInvalid(String.format(this.minText, this.minValue));
44107             return false;
44108         }
44109         
44110         if(num > this.maxValue){
44111             this.markInvalid(String.format(this.maxText, this.maxValue));
44112             return false;
44113         }
44114         
44115         return true;
44116     },
44117     
44118     validate : function()
44119     {
44120         if(this.disabled || this.allowBlank){
44121             this.markValid();
44122             return true;
44123         }
44124         
44125         var currency = this.getCurrency();
44126         
44127         if(this.validateValue(this.getRawValue()) && currency.length){
44128             this.markValid();
44129             return true;
44130         }
44131         
44132         this.markInvalid();
44133         return false;
44134     },
44135     
44136     getName: function()
44137     {
44138         return this.name;
44139     },
44140     
44141     beforeBlur : function()
44142     {
44143         if(!this.castInt){
44144             return;
44145         }
44146         
44147         var v = this.parseValue(this.getRawValue());
44148         
44149         if(v || v == 0){
44150             this.setValue(v);
44151         }
44152     },
44153     
44154     onBlur : function()
44155     {
44156         this.beforeBlur();
44157         
44158         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44159             //this.el.removeClass(this.focusClass);
44160         }
44161         
44162         this.hasFocus = false;
44163         
44164         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44165             this.validate();
44166         }
44167         
44168         var v = this.getValue();
44169         
44170         if(String(v) !== String(this.startValue)){
44171             this.fireEvent('change', this, v, this.startValue);
44172         }
44173         
44174         this.fireEvent("blur", this);
44175     },
44176     
44177     inputEl : function()
44178     {
44179         return this.el.select('.roo-money-amount-input', true).first();
44180     },
44181     
44182     currencyEl : function()
44183     {
44184         return this.el.select('.roo-money-currency-input', true).first();
44185     },
44186     
44187     hiddenEl : function()
44188     {
44189         return this.el.select('input.hidden-number-input',true).first();
44190     }
44191     
44192 });/**
44193  * @class Roo.bootstrap.BezierSignature
44194  * @extends Roo.bootstrap.Component
44195  * Bootstrap BezierSignature class
44196  * This script refer to:
44197  *    Title: Signature Pad
44198  *    Author: szimek
44199  *    Availability: https://github.com/szimek/signature_pad
44200  *
44201  * @constructor
44202  * Create a new BezierSignature
44203  * @param {Object} config The config object
44204  */
44205
44206 Roo.bootstrap.BezierSignature = function(config){
44207     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44208     this.addEvents({
44209         "resize" : true
44210     });
44211 };
44212
44213 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44214 {
44215      
44216     curve_data: [],
44217     
44218     is_empty: true,
44219     
44220     mouse_btn_down: true,
44221     
44222     /**
44223      * @cfg {int} canvas height
44224      */
44225     canvas_height: '200px',
44226     
44227     /**
44228      * @cfg {float|function} Radius of a single dot.
44229      */ 
44230     dot_size: false,
44231     
44232     /**
44233      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44234      */
44235     min_width: 0.5,
44236     
44237     /**
44238      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44239      */
44240     max_width: 2.5,
44241     
44242     /**
44243      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44244      */
44245     throttle: 16,
44246     
44247     /**
44248      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44249      */
44250     min_distance: 5,
44251     
44252     /**
44253      * @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.
44254      */
44255     bg_color: 'rgba(0, 0, 0, 0)',
44256     
44257     /**
44258      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44259      */
44260     dot_color: 'black',
44261     
44262     /**
44263      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44264      */ 
44265     velocity_filter_weight: 0.7,
44266     
44267     /**
44268      * @cfg {function} Callback when stroke begin. 
44269      */
44270     onBegin: false,
44271     
44272     /**
44273      * @cfg {function} Callback when stroke end.
44274      */
44275     onEnd: false,
44276     
44277     getAutoCreate : function()
44278     {
44279         var cls = 'roo-signature column';
44280         
44281         if(this.cls){
44282             cls += ' ' + this.cls;
44283         }
44284         
44285         var col_sizes = [
44286             'lg',
44287             'md',
44288             'sm',
44289             'xs'
44290         ];
44291         
44292         for(var i = 0; i < col_sizes.length; i++) {
44293             if(this[col_sizes[i]]) {
44294                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44295             }
44296         }
44297         
44298         var cfg = {
44299             tag: 'div',
44300             cls: cls,
44301             cn: [
44302                 {
44303                     tag: 'div',
44304                     cls: 'roo-signature-body',
44305                     cn: [
44306                         {
44307                             tag: 'canvas',
44308                             cls: 'roo-signature-body-canvas',
44309                             height: this.canvas_height,
44310                             width: this.canvas_width
44311                         }
44312                     ]
44313                 },
44314                 {
44315                     tag: 'input',
44316                     type: 'file',
44317                     style: 'display: none'
44318                 }
44319             ]
44320         };
44321         
44322         return cfg;
44323     },
44324     
44325     initEvents: function() 
44326     {
44327         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44328         
44329         var canvas = this.canvasEl();
44330         
44331         // mouse && touch event swapping...
44332         canvas.dom.style.touchAction = 'none';
44333         canvas.dom.style.msTouchAction = 'none';
44334         
44335         this.mouse_btn_down = false;
44336         canvas.on('mousedown', this._handleMouseDown, this);
44337         canvas.on('mousemove', this._handleMouseMove, this);
44338         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44339         
44340         if (window.PointerEvent) {
44341             canvas.on('pointerdown', this._handleMouseDown, this);
44342             canvas.on('pointermove', this._handleMouseMove, this);
44343             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44344         }
44345         
44346         if ('ontouchstart' in window) {
44347             canvas.on('touchstart', this._handleTouchStart, this);
44348             canvas.on('touchmove', this._handleTouchMove, this);
44349             canvas.on('touchend', this._handleTouchEnd, this);
44350         }
44351         
44352         Roo.EventManager.onWindowResize(this.resize, this, true);
44353         
44354         // file input event
44355         this.fileEl().on('change', this.uploadImage, this);
44356         
44357         this.clear();
44358         
44359         this.resize();
44360     },
44361     
44362     resize: function(){
44363         
44364         var canvas = this.canvasEl().dom;
44365         var ctx = this.canvasElCtx();
44366         var img_data = false;
44367         
44368         if(canvas.width > 0) {
44369             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44370         }
44371         // setting canvas width will clean img data
44372         canvas.width = 0;
44373         
44374         var style = window.getComputedStyle ? 
44375             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44376             
44377         var padding_left = parseInt(style.paddingLeft) || 0;
44378         var padding_right = parseInt(style.paddingRight) || 0;
44379         
44380         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44381         
44382         if(img_data) {
44383             ctx.putImageData(img_data, 0, 0);
44384         }
44385     },
44386     
44387     _handleMouseDown: function(e)
44388     {
44389         if (e.browserEvent.which === 1) {
44390             this.mouse_btn_down = true;
44391             this.strokeBegin(e);
44392         }
44393     },
44394     
44395     _handleMouseMove: function (e)
44396     {
44397         if (this.mouse_btn_down) {
44398             this.strokeMoveUpdate(e);
44399         }
44400     },
44401     
44402     _handleMouseUp: function (e)
44403     {
44404         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44405             this.mouse_btn_down = false;
44406             this.strokeEnd(e);
44407         }
44408     },
44409     
44410     _handleTouchStart: function (e) {
44411         
44412         e.preventDefault();
44413         if (e.browserEvent.targetTouches.length === 1) {
44414             // var touch = e.browserEvent.changedTouches[0];
44415             // this.strokeBegin(touch);
44416             
44417              this.strokeBegin(e); // assume e catching the correct xy...
44418         }
44419     },
44420     
44421     _handleTouchMove: function (e) {
44422         e.preventDefault();
44423         // var touch = event.targetTouches[0];
44424         // _this._strokeMoveUpdate(touch);
44425         this.strokeMoveUpdate(e);
44426     },
44427     
44428     _handleTouchEnd: function (e) {
44429         var wasCanvasTouched = e.target === this.canvasEl().dom;
44430         if (wasCanvasTouched) {
44431             e.preventDefault();
44432             // var touch = event.changedTouches[0];
44433             // _this._strokeEnd(touch);
44434             this.strokeEnd(e);
44435         }
44436     },
44437     
44438     reset: function () {
44439         this._lastPoints = [];
44440         this._lastVelocity = 0;
44441         this._lastWidth = (this.min_width + this.max_width) / 2;
44442         this.canvasElCtx().fillStyle = this.dot_color;
44443     },
44444     
44445     strokeMoveUpdate: function(e)
44446     {
44447         this.strokeUpdate(e);
44448         
44449         if (this.throttle) {
44450             this.throttleStroke(this.strokeUpdate, this.throttle);
44451         }
44452         else {
44453             this.strokeUpdate(e);
44454         }
44455     },
44456     
44457     strokeBegin: function(e)
44458     {
44459         var newPointGroup = {
44460             color: this.dot_color,
44461             points: []
44462         };
44463         
44464         if (typeof this.onBegin === 'function') {
44465             this.onBegin(e);
44466         }
44467         
44468         this.curve_data.push(newPointGroup);
44469         this.reset();
44470         this.strokeUpdate(e);
44471     },
44472     
44473     strokeUpdate: function(e)
44474     {
44475         var rect = this.canvasEl().dom.getBoundingClientRect();
44476         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44477         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44478         var lastPoints = lastPointGroup.points;
44479         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44480         var isLastPointTooClose = lastPoint
44481             ? point.distanceTo(lastPoint) <= this.min_distance
44482             : false;
44483         var color = lastPointGroup.color;
44484         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44485             var curve = this.addPoint(point);
44486             if (!lastPoint) {
44487                 this.drawDot({color: color, point: point});
44488             }
44489             else if (curve) {
44490                 this.drawCurve({color: color, curve: curve});
44491             }
44492             lastPoints.push({
44493                 time: point.time,
44494                 x: point.x,
44495                 y: point.y
44496             });
44497         }
44498     },
44499     
44500     strokeEnd: function(e)
44501     {
44502         this.strokeUpdate(e);
44503         if (typeof this.onEnd === 'function') {
44504             this.onEnd(e);
44505         }
44506     },
44507     
44508     addPoint:  function (point) {
44509         var _lastPoints = this._lastPoints;
44510         _lastPoints.push(point);
44511         if (_lastPoints.length > 2) {
44512             if (_lastPoints.length === 3) {
44513                 _lastPoints.unshift(_lastPoints[0]);
44514             }
44515             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44516             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44517             _lastPoints.shift();
44518             return curve;
44519         }
44520         return null;
44521     },
44522     
44523     calculateCurveWidths: function (startPoint, endPoint) {
44524         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44525             (1 - this.velocity_filter_weight) * this._lastVelocity;
44526
44527         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44528         var widths = {
44529             end: newWidth,
44530             start: this._lastWidth
44531         };
44532         
44533         this._lastVelocity = velocity;
44534         this._lastWidth = newWidth;
44535         return widths;
44536     },
44537     
44538     drawDot: function (_a) {
44539         var color = _a.color, point = _a.point;
44540         var ctx = this.canvasElCtx();
44541         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44542         ctx.beginPath();
44543         this.drawCurveSegment(point.x, point.y, width);
44544         ctx.closePath();
44545         ctx.fillStyle = color;
44546         ctx.fill();
44547     },
44548     
44549     drawCurve: function (_a) {
44550         var color = _a.color, curve = _a.curve;
44551         var ctx = this.canvasElCtx();
44552         var widthDelta = curve.endWidth - curve.startWidth;
44553         var drawSteps = Math.floor(curve.length()) * 2;
44554         ctx.beginPath();
44555         ctx.fillStyle = color;
44556         for (var i = 0; i < drawSteps; i += 1) {
44557         var t = i / drawSteps;
44558         var tt = t * t;
44559         var ttt = tt * t;
44560         var u = 1 - t;
44561         var uu = u * u;
44562         var uuu = uu * u;
44563         var x = uuu * curve.startPoint.x;
44564         x += 3 * uu * t * curve.control1.x;
44565         x += 3 * u * tt * curve.control2.x;
44566         x += ttt * curve.endPoint.x;
44567         var y = uuu * curve.startPoint.y;
44568         y += 3 * uu * t * curve.control1.y;
44569         y += 3 * u * tt * curve.control2.y;
44570         y += ttt * curve.endPoint.y;
44571         var width = curve.startWidth + ttt * widthDelta;
44572         this.drawCurveSegment(x, y, width);
44573         }
44574         ctx.closePath();
44575         ctx.fill();
44576     },
44577     
44578     drawCurveSegment: function (x, y, width) {
44579         var ctx = this.canvasElCtx();
44580         ctx.moveTo(x, y);
44581         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44582         this.is_empty = false;
44583     },
44584     
44585     clear: function()
44586     {
44587         var ctx = this.canvasElCtx();
44588         var canvas = this.canvasEl().dom;
44589         ctx.fillStyle = this.bg_color;
44590         ctx.clearRect(0, 0, canvas.width, canvas.height);
44591         ctx.fillRect(0, 0, canvas.width, canvas.height);
44592         this.curve_data = [];
44593         this.reset();
44594         this.is_empty = true;
44595     },
44596     
44597     fileEl: function()
44598     {
44599         return  this.el.select('input',true).first();
44600     },
44601     
44602     canvasEl: function()
44603     {
44604         return this.el.select('canvas',true).first();
44605     },
44606     
44607     canvasElCtx: function()
44608     {
44609         return this.el.select('canvas',true).first().dom.getContext('2d');
44610     },
44611     
44612     getImage: function(type)
44613     {
44614         if(this.is_empty) {
44615             return false;
44616         }
44617         
44618         // encryption ?
44619         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44620     },
44621     
44622     drawFromImage: function(img_src)
44623     {
44624         var img = new Image();
44625         
44626         img.onload = function(){
44627             this.canvasElCtx().drawImage(img, 0, 0);
44628         }.bind(this);
44629         
44630         img.src = img_src;
44631         
44632         this.is_empty = false;
44633     },
44634     
44635     selectImage: function()
44636     {
44637         this.fileEl().dom.click();
44638     },
44639     
44640     uploadImage: function(e)
44641     {
44642         var reader = new FileReader();
44643         
44644         reader.onload = function(e){
44645             var img = new Image();
44646             img.onload = function(){
44647                 this.reset();
44648                 this.canvasElCtx().drawImage(img, 0, 0);
44649             }.bind(this);
44650             img.src = e.target.result;
44651         }.bind(this);
44652         
44653         reader.readAsDataURL(e.target.files[0]);
44654     },
44655     
44656     // Bezier Point Constructor
44657     Point: (function () {
44658         function Point(x, y, time) {
44659             this.x = x;
44660             this.y = y;
44661             this.time = time || Date.now();
44662         }
44663         Point.prototype.distanceTo = function (start) {
44664             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44665         };
44666         Point.prototype.equals = function (other) {
44667             return this.x === other.x && this.y === other.y && this.time === other.time;
44668         };
44669         Point.prototype.velocityFrom = function (start) {
44670             return this.time !== start.time
44671             ? this.distanceTo(start) / (this.time - start.time)
44672             : 0;
44673         };
44674         return Point;
44675     }()),
44676     
44677     
44678     // Bezier Constructor
44679     Bezier: (function () {
44680         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44681             this.startPoint = startPoint;
44682             this.control2 = control2;
44683             this.control1 = control1;
44684             this.endPoint = endPoint;
44685             this.startWidth = startWidth;
44686             this.endWidth = endWidth;
44687         }
44688         Bezier.fromPoints = function (points, widths, scope) {
44689             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44690             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44691             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44692         };
44693         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44694             var dx1 = s1.x - s2.x;
44695             var dy1 = s1.y - s2.y;
44696             var dx2 = s2.x - s3.x;
44697             var dy2 = s2.y - s3.y;
44698             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44699             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44700             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44701             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44702             var dxm = m1.x - m2.x;
44703             var dym = m1.y - m2.y;
44704             var k = l2 / (l1 + l2);
44705             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44706             var tx = s2.x - cm.x;
44707             var ty = s2.y - cm.y;
44708             return {
44709                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44710                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44711             };
44712         };
44713         Bezier.prototype.length = function () {
44714             var steps = 10;
44715             var length = 0;
44716             var px;
44717             var py;
44718             for (var i = 0; i <= steps; i += 1) {
44719                 var t = i / steps;
44720                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44721                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44722                 if (i > 0) {
44723                     var xdiff = cx - px;
44724                     var ydiff = cy - py;
44725                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44726                 }
44727                 px = cx;
44728                 py = cy;
44729             }
44730             return length;
44731         };
44732         Bezier.prototype.point = function (t, start, c1, c2, end) {
44733             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44734             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44735             + (3.0 * c2 * (1.0 - t) * t * t)
44736             + (end * t * t * t);
44737         };
44738         return Bezier;
44739     }()),
44740     
44741     throttleStroke: function(fn, wait) {
44742       if (wait === void 0) { wait = 250; }
44743       var previous = 0;
44744       var timeout = null;
44745       var result;
44746       var storedContext;
44747       var storedArgs;
44748       var later = function () {
44749           previous = Date.now();
44750           timeout = null;
44751           result = fn.apply(storedContext, storedArgs);
44752           if (!timeout) {
44753               storedContext = null;
44754               storedArgs = [];
44755           }
44756       };
44757       return function wrapper() {
44758           var args = [];
44759           for (var _i = 0; _i < arguments.length; _i++) {
44760               args[_i] = arguments[_i];
44761           }
44762           var now = Date.now();
44763           var remaining = wait - (now - previous);
44764           storedContext = this;
44765           storedArgs = args;
44766           if (remaining <= 0 || remaining > wait) {
44767               if (timeout) {
44768                   clearTimeout(timeout);
44769                   timeout = null;
44770               }
44771               previous = now;
44772               result = fn.apply(storedContext, storedArgs);
44773               if (!timeout) {
44774                   storedContext = null;
44775                   storedArgs = [];
44776               }
44777           }
44778           else if (!timeout) {
44779               timeout = window.setTimeout(later, remaining);
44780           }
44781           return result;
44782       };
44783   }
44784   
44785 });
44786
44787  
44788
44789